# Troubleshooting (/docs/haus/reference/troubleshooting)



Almost every haus problem is one of a few known macOS quirks with a known fix.
Start here, whatever the symptom:

```sh
haus doctor      # the whole machine in one pass
haus services    # every background job, and where each one logs
```

`haus doctor` catches a job whose last run failed, not only one that is dead
right now, so a cleanup quietly exiting non-zero every Sunday gets a line.
[`haus services`](/docs/haus/reference/haus#what-needs-a-person-and-what-runs-itself)
prints each log's real path: the tiler, the bar and the launcher write
`<name>.out.log` and `<name>.err.log` in `/tmp`, a job from another room under
`~/Library/Logs` or `/var/log`.

## ⌘Space does nothing [#space-does-nothing]

Pounce is a launchd **user agent**. If it isn't answering, kick it. This is the
clean recovery for *any* wedged agent, and the rest of this page reuses it with
a different label:

```sh
launchctl list | grep pounce   # is it running at all?
launchctl kickstart -k gui/$(id -u)/com.hausfold.pounce
```

If the palette opens but **clipboard and emoji paste** don't work, it's missing
the Accessibility grant:

```sh
pounce --request-accessibility   # the daemon raises the dialog
pounce --check-accessibility     # true, false, or unknown with no daemon
```

Both questions go to the running daemon, because the daemon is the process that
holds the grant. With nothing running they print `unknown` rather than guess, so
kickstart it first. Older pounce releases answered for the *terminal* you typed
in, so the check could print `true` while the palette had no grant at all: if it
looks granted and paste is still dead, upgrade before you chase it any further.

The grant survives rebuilds on its own; see [Granting
Accessibility](/docs/pounce/install#grant-accessibility). If ⌘Space still opens
**Spotlight**, log out and back in once so the symbolic-hotkey reassignment
takes.

<Callout title="Palette opens, but slowly?">
  Another hotkey tool bound to the same key can grab it ahead of Pounce, and
  registration still reports success, which is what makes it hard to spot.
  [`pounce doctor`](/docs/pounce/install#check-it-landed) names the culprit.

  One line of its output is wrong here: if the daemon isn't running it suggests
  `brew services start pounce`, the advice for a standalone Homebrew install. On
  this Mac the daemon is the launchd agent above; kick that instead.
</Callout>

## Windows won't tile [#windows-wont-tile]

Almost always the **cold-boot race**: an agent that launched before the desktop
session was ready parked itself instead of starting. Kick it:

```sh
launchctl kickstart -k gui/$(id -u)/org.nixos.aerospace
```

If the kick doesn't take, check the Accessibility grant, because an ungranted
AeroSpace looks dead rather than ungranted: it starts, stays running, and never
opens its socket, so `aerospace --version` answers that the server is not
responding and every other verb dies on `Connection refused`. `haus permissions`
carries the card, with the System Settings pane to tick and the kickstart above
to run once you have.

The smaller ones:

* **Scrambled after sleep, or a window on the wrong workspace?** Tap ⇪ then
  <kbd>\`</kbd> to re-sort everything to its roster home.
* **A binding you changed didn't take?** A rebuild rewrites the config, but the
  live daemon holds the old one until it reloads. Force it with `aerospace
  reload-config`, or run **Reload AeroSpace** from the ⌘Space palette.

## The bar is blank or missing [#the-bar-is-blank-or-missing]

```sh
launchctl kickstart -k gui/$(id -u)/org.nixos.sketchybar
launchctl kickstart -k gui/$(id -u)/org.nixos.bar-bottom   # if you run the bottom bar
```

If `/tmp/sketchybar.err.log` says `could not acquire lock-file … already
running?`, a stray `brew services` copy of SketchyBar is holding the lock. Every
rebuild evicts it, so `haus rebuild` is the fix.

**Running, but stale** (a pill you added isn't there, or a colour change didn't
land)? The easy fix is **Reload SketchyBar**, from the ⌘Space palette or the
haus menu on a left-click of the logo pill. By hand:

```sh
sketchybar  --reload ~/.config/sketchybar/sketchybarrc    # the menu-bar bar
bar-bottom --reload ~/.config/sketchybar/bar-bottomrc   # the bottom bar, if enabled
```

<Callout type="warn" title="Always name the rc, and reload each bar">
  `--reload` on its own doesn't mean "re-read your config"; it means *re-run the
  path you resolved at startup*, and that path is a symlink into `/nix/store`
  resolved once. So a bare reload can quietly replay the config from the
  generation the bar booted on while exiting 0 and logging success. Naming the
  path re-resolves it now. And the two bars are separate instances: a lone
  `sketchybar --reload` leaves the bottom one exactly as it was.
</Callout>

Not wanting a bar at all is a different question: `haus.bar.enable = false`
brings the native macOS menu bar back.

## After a macOS upgrade, *every* agent is dead [#after-a-macos-upgrade-every-agent-is-dead]

If the bar, tiling and ⌘Space all come up dead at once right after upgrading to
**macOS 26 Tahoe or later**, the culprit is **Background Task Management**.
Tahoe gates login items whose executable isn't Apple-signed, and every Nix agent
launches through `/bin/sh -c "…"`, which BTM files under "unidentified
developer" and can silently refuse to start. They register fine; they just never
run.

```sh
haus btm    # no-op before Tahoe; on Tahoe+ it reads the BTM store and instructs
```

There is **no declarative fix**: the toggle lives in macOS's own BTM store,
which has no CLI to set it, so it is a one-time manual step. In **System
Settings ▸ General ▸ Login Items & Extensions**, scroll to **Allow in the
Background*&#x2A;, find the entries named &#x2A;*`sh`** (subtitled *Item from unidentified
developer*), switch them on and reboot. Already on but still blocked? Flip off,
then on, to force a database write.

To read the store yourself:

```sh
sudo sfltool dumpbtm | grep -B2 -A8 -iE "nixos|hausfold|darwin-store"
```

Look for `disallowed`, and search all three names: the launcher registers under
`com.hausfold.pounce` while tiling and the bar are still `org.nixos.*`, so a
grep for `nixos` alone reports a clean store while the launcher is the thing
that's blocked.

## Every agent is dead, and macOS didn't change [#every-agent-is-dead-and-macos-didnt-change]

Same symptom, different cause, and this one needs no upgrade: &#x2A;*a rebuild that
aborted before it got to them.** If your host file writes
`system.defaults.universalaccess.*`, activation writes those keys two thirds of
the way through, and without Full Disk Access that write fails, takes the rest
of activation with it, and never reaches the launchd agents. The failure is
nowhere near the symptom.

`haus permissions` walks you through granting it and `haus plan` says whether
the next rebuild needs it. Better still, stop writing the domain by hand:
`haus.accessibility.*` writes the same keys guarded, so a missing grant costs
you that setting and nothing else. [The one rebuild it
refuses](/docs/haus/reference/haus#the-one-rebuild-it-refuses) has the whole
rule.

## Touch ID for sudo beachballs inside a multiplexer [#touch-id-for-sudo-beachballs-inside-a-multiplexer]

The `pam_reattach` shim isn't loading. It ships with the **security** room and
is on by default; check the order in `/etc/pam.d/sudo_local`, where reattach
must come *before* the Touch ID line. Outside a multiplexer, Touch ID should
just work; cancel the prompt to fall back to your password. [Touch
ID](/docs/haus/rooms/security) has the rest.

## "Refusing to load cask … from an untrusted tap" [#refusing-to-load-cask--from-an-untrusted-tap]

Homebrew refuses a cask from a third-party tap nothing has trusted, and the
`brew trust` you type by hand never reaches the sudo-driven activation a rebuild
runs. Trust has to be on the **tap** line: a cask's own stamp is thrown away
unless the cask is spelled `owner/repo/cask`, so the tap is what decides it.

```nix
homebrew.taps = [ { name = "owner/tap"; trusted = true; } ];
```

Every tap haus ships already says this. A rebuild warns about any tap in *your*
config that doesn't, which is the warning to act on before a cold install does
it for you: `brew bundle` runs last, so a refused cask takes the whole user half
of an install with it and leaves a Mac that looks untouched. When that has
happened, `haus rebuild` exits non-zero at the end and `haus doctor` names the
casks that are declared but absent.

## An app I removed from my config is still installed [#an-app-i-removed-from-my-config-is-still-installed]

By design. `haus.homebrew.cleanup` is `"none"&#x60;, so nothing you installed is ever
auto-deleted, and **`haus rollback` doesn't rewind Homebrew apps**, because they
aren't in Nix generations. Remove one by hand:

```sh
brew uninstall --zap <cask>
```

For a machine where an undeclared cask *is* removed on the next rebuild, set
[Customize a desktop](/docs/haus/desktops/customizing#homebrew-behaviour).

## A rebuild broke something [#a-rebuild-broke-something]

```sh
haus rollback       # atomically back to the previous generation
haus generations    # what you can roll back to
```

For macOS **settings** rather than packages, `haus revert-settings` puts back
the snapshot `haus capture` took, and the APFS snapshot from install time is the
coarser rewind. To go further than one generation (switching a room off, or
removing haus entirely), [Leaving](/docs/haus/leaving) walks each exit, smallest
first.

## "Found a Nix at /nix that isn't Determinate" [#found-a-nix-at-nix-that-isnt-determinate]

haus is built on [Determinate Nix](https://docs.determinate.systems/), and the
installer stops on any `/nix` that Determinate didn't create rather than risk
breaking the Nix you already have. It checks the *flavour*, so single-user and
multi-user daemon installs stop it alike. Uninstall the Nix you don't need and
re-run the one-liner, or migrate it to Determinate, which is the supported path.
A fresh Mac needs none of this.

## Still stuck? [#still-stuck]

{/* Deliberately in-body rather than frontmatter `related:` (which renders
    below prev/next): a closing paragraph follows these cards, and they are
    part of this section's argument, not a way onward. */}

<Cards>
  <Card icon="<Icon name=&#x22;wrench&#x22; />" title="The haus CLI" href="/docs/haus/reference/haus" description="doctor, plan, diff, capture: the read-only commands that show you what a change would do before it does it." />

  <Card icon="<Icon name=&#x22;dials&#x22; />" title="Customize a desktop" href="/docs/haus/desktops/customizing" description="Every dial worth knowing, and the rooms you can switch off when one of them isn't for you." />
</Cards>

Still nothing? `haus report` fills haus's bug form in for you, so all that's
left to write is what happened. On a full machine the report is too long to ride
in a link: the form opens empty and the block goes to your terminal, or your
clipboard if you came from the palette, to paste in. Nothing is filed until you
press Submit, and `haus report --print` opens no browser at all.
