Troubleshooting
Find the symptom, check what the app reports, and follow the recovery steps.
Start with Setup & diagnostics. Installation, login, permissions and project checks are separate: a successful result in one does not prove the others work.
Start with Setup & diagnostics
Open Help → Setup & diagnostics, choose your project and agent, then click Check setup. The checks cover folder access, Git, CLI version, configured MCP connections, the Browser and the test engine. A successful setup check does not prove that your project works. The separate Test CLI button makes a real request using your provider account.
For support, choose Preview support report. Review the JSON before saving it. It contains the running build identity, resource counters, permission preflight and cached CLI/engine checks with their age. Export does not start new CLI or engine probes. Missing checks remain unknown; an available native payload does not mean permission was granted or a physical action succeeded.
Only built-in CLIs checked in the current app process appear in the report. Versions contain the numeric release core; custom names, paths, credentials, raw errors, transcripts and project contents are excluded. Saving writes exactly the report you previewed. Nothing is uploaded automatically. Re-run the relevant setup check and create a new preview if you need a fresh sample.
Inspect a local log when needed
The local event log can help explain a failure after the setup checks. It is separate from the filtered support report; inspect its contents before sharing any excerpt.
| Platform | Log file |
|---|---|
| macOS | ~/Library/Logs/DeckSpace/jarvis.log |
| Linux | $XDG_STATE_HOME/deckspace/jarvis.log, defaulting to ~/.local/state/deckspace/jarvis.log |
| Windows | No dedicated location. The path is derived from HOME, which Windows normally does not set, so the file lands in the system temp directory under DeckSpace-logs. |
Embedded newlines are collapsed to ⏎, so every event stays on one line and grep works.
An agent CLI is not detected
Symptom. claude --version works in your terminal, but the CLI shows up grey in Detect installed AI CLIs and DeckSpace will not launch it.
Why. A GUI app launched from Finder or the Dock inherits launchd’s environment, not your shell’s. The minimal PATH it gets has no nvm, asdf, mise, Homebrew or cargo shims in it.
DeckSpace already works around this. Once per app launch it runs $SHELL -lic 'printf %s "$PATH"', merges the result with the directories below, caches the whole thing, and uses it for every pane and every probe.
| Always searched, whatever your shell says |
|---|
~/.local/bin · ~/.grok/bin · ~/.opencode/bin · ~/.cargo/bin · ~/.bun/bin · ~/.npm-global/bin · ~/.deno/bin · ~/.volta/bin · ~/.local/share/mise/shims · ~/bin · /opt/homebrew/bin · /usr/local/bin · /usr/bin · /bin |
Three things break that workaround:
| Cause | What you see | Fix |
|---|---|---|
Your login shell takes longer than 1.5 seconds to print its PATH. The query is killed at that cap so a slow .zshrc can never block the first pane. | Only the fixed list above is searched. Anything installed elsewhere is invisible. | Register the CLI by absolute path, or move it onto the fixed list. |
| The CLI installed itself somewhere neither your shell nor the fixed list mentions. | Same. | Same. |
| The installer added a new directory to your shell’s PATH while DeckSpace was running. | The cached PATH does not include that directory yet. A new binary in an already-searched directory can be found on the next scan. | Rescan first. If the installation changed PATH, quit and relaunch DeckSpace, then scan again. |
Register it by absolute path. Settings → Custom agent CLIs takes a name, a key, and a binary — the binary field accepts either a bare command or a full path such as /usr/local/bin/goose. An absolute path is checked directly and skips PATH resolution entirely.
The detailed CLI probe runs <path> --version with a 3-second cap. A binary can be installed while its version check fails or times out. Setup & diagnostics reports that probe error separately; use Test CLI to check an actual request after confirming your login.
An export ANTHROPIC_API_KEY=… in ~/.zshenv is invisible to a GUI app for the same reason. DeckSpace copies missing variables out of your login shell with env -0, under the same 1.5-second cap, and only for keys the app does not already have — PATH, PWD, OLDPWD, SHLVL and _ are always skipped. If your shell is slow to start, nothing is copied and the agent will fail to authenticate with no explanation on screen.
A pane opens on walk-away and sits there
Symptom. Walk-away is on, the pane launches, the agent starts — and then stops at its own permission prompt and waits for a human who has left.
Why. DeckSpace does not implement autonomy. It passes the flag your CLI publishes for it, and five of the built-in eleven publish none.
| Agent CLI | Walk-away flag |
|---|---|
| Aider | none — the toggle does nothing |
| Cursor Agent | none — the toggle does nothing |
| Droid | none — the toggle does nothing |
| Copilot CLI | none — the toggle does nothing |
| Amp | none — the toggle does nothing |
Fix. Use one of the six that do have a flag — see What walk-away does for the full table — or register the CLI again under Settings → Custom agent CLIs and fill in the Walk-away flag field yourself. A custom agent with that field empty behaves exactly like the five above.
An agent’s command was refused
Symptom. The agent runs something ordinary and gets back a red line beginning ⚠ DeckSpace, or a plain Operation not permitted.
These are two different mechanisms with two different fixes. Read which message you got.
“DeckSpace guard” / “DeckSpace blocked a destructive git”
Agent-driven panes get OPX_GUARD=1 and a shim directory at the front of PATH holding three files: rm, git and sudo. The shims are inert without that variable, so a pane you typed into yourself is unaffected. They parse arguments rather than matching text, so flag spellings do not slip past.
| What the agent ran | What happened |
|---|---|
rm -r, rm -rf, rm -rfv, rm --recursive | Targets moved to ~/.opxspace-trash, not deleted. Yellow warning, exit 0 — the agent believes it succeeded. |
A recursive rm where some target could not be moved | Nothing is deleted. Red warning, exit 1. |
git reset --hard | Refused, exit 1 |
git clean -f / --force | Refused |
git push -f, --force, --force-with-lease, or a +ref refspec | Refused |
git checkout ., git checkout -f | Refused |
git restore — with any arguments at all | Refused |
git switch --discard-changes / -f | Refused |
git branch -D / --delete | Refused |
git stash drop, git stash clear | Refused |
git worktree remove | Refused |
git update-ref -d | Refused |
git reflog expire / delete | Refused |
git filter-branch, git filter-repo | Refused |
git gc --prune=now | Refused |
sudo — anything | Refused |
Fix. The refusal message prints the absolute path of the real binary. Run the command yourself in a pane you opened by hand, or call that absolute path. Deleted work is not gone: look in ~/.opxspace-trash first.
git restore is the entry that surprises people. It is refused unconditionally, including harmless forms like git restore --staged <file>, because the shim keys on the subcommand and not on the flags after it.
“Operation not permitted”
That is the OS sandbox, not DeckSpace. An agent launched into its own pane runs inside a write fence: reads, execution and network are free, all file writes are denied, and writes are then re-allowed in a short list of places.
The fence covers agents DeckSpace starts — in their own pane, in a pane it opens for you (swarm builders, Maestro workers, Kanban runs), and work it runs with no pane at all. A terminal you opened yourself is deliberately not fenced, so if you see this error in an agent pane and not in your own shell, that is why. The full table is on the walk-away page.
| Writable inside an agent pane |
|---|
| The workspace it is working in |
/private/tmp, /private/var/tmp, /private/var/folders, /dev |
~/.claude, ~/.codex, ~/.gemini, ~/.config, ~/.cache, ~/.local/state, ~/.npm, ~/.opxspace-trash |
Everything else is refused by the kernel — whether the agent writes directly, shells out, uses an absolute path, or runs a script. Another repo on the same machine is outside the fence.
Fix. Move the work into the workspace, or turn Confine agent writes off under Settings → Autonomy for the run that legitimately needs to write elsewhere.
There is no sandbox on this machine
| Platform | Backend | Condition |
|---|---|---|
| macOS | sandbox-exec | available when /usr/bin/sandbox-exec exists |
| Linux | bwrap | available only if bwrap --version succeeds |
| Windows | restricted token | reported only after the local fence self-test succeeds on this machine |
The Confine agent writes switch is a preference. It does not create a missing sandbox. Read the explanation under it in Settings → Autonomy: it names the available backend, or states that agents can write anywhere you can. An agent may still launch without a backend.
On Linux, install bubblewrap if needed. On Windows, an unsuccessful local restricted-token test means no fence is reported. Review the machine’s configuration before relying on confinement. The command guard is a separate protection and does not establish a filesystem fence.
The app never offers an update
Open Settings → Updates and press Check for updates. It reports an available version, a successful no-update result, or a connection error. A failed check is not shown as “up to date”. The manual check works even when background checks are switched off.
Background checks run after launch, periodically while the app is awake, and on focus, with a 30-minute minimum interval. Use the visible checkbox to turn them off or on. Nothing installs until you choose Update now; a failed install stays available to retry.
Preview and development builds use manual installation and do not query the public update channel. Linux .deb and .rpm installations also use manual package installation. Download the current package from your delivery link and install it with your package manager. The update panel explains these cases without claiming a successful update check.
Compare the installed version with Updating and the release notes if needed. A detached panel does not run the main window’s automatic check cadence.
The phone remote answers 403
Two entirely different refusals share that status code, and they send you to two different screens. The error.code in the response body is what tells them apart.
| Code | What it means | Fix |
|---|---|---|
forbidden_scope | The device was not paired with the scope this route needs. The message names it: this route needs the order scope. The five scopes are monitor, order, approve, converse and drive (shown as Type into panes). | Pair the phone again. Scopes can be taken away from the device list in Settings → Remote and that takes effect immediately — but they cannot be added there. The checkbox for a scope a device does not hold is disabled on purpose. |
forbidden_scope, message this device may not take screenshots | Screen captures are a per-device flag, not a scope, because a capture shows your whole screen and not only DeckSpace. | Same — granted at pair time only. |
forbidden_permission | The device does hold the scope. A tool-permission category on the desktop is set to ask or deny, and a confirm arriving from a phone has no prompt anyone is standing at. | Set that category to allow on the desktop. The body names it in detail.category. |
pairing_closed | You typed a pairing code with no pairing window open, or with Remote switched off. | Open Settings → Remote on the desktop and start pairing there first. |
Only two categories can produce forbidden_permission, and which one depends on where the order was told to land:
| Order target | Category that must be allow |
|---|---|
| A board task (the default) | Board write |
| A headless mission | Shell commands — the mission runs an agent that writes files and runs commands in the repo |
The message the phone renders says to set the category in Settings → Permissions. The section is actually titled Tool permissions · pre-authorization. Same screen, and the category labels in it (Board write, Shell commands) match the message exactly.
The other refusals a phone can hit:
| Status | Code | Cause |
|---|---|---|
| 401 | pairing_invalid | The code is unknown, expired, or already used. |
| 409 | device_limit | Eight devices are already paired. Remove one first. |
| 409 | already_decided | The gate moved on between the list and your tap. Refetch. |
| 429 | — | 120 requests per minute per device. Five failed authentications lock that device out for 15 minutes. |
| 503 | renderer_unavailable | The DeckSpace window is closed on the desktop. |
A gate never reaches the phone
Symptom. Something is clearly waiting for a human on the desktop, and the phone’s approvals list is empty.
Three of the four gate kinds live only inside the DeckSpace window — the Maestro plan gate, the Maestro question wizard, and mission approvals. They are listed where they can be decided, so while the window is closed they are simply absent rather than shown and un-tappable. Reopen the window and the gate reappears with its original timestamp.
Every DeckSpace window is its own context. A mission started in a detached Loop panel keeps its approval handles in that window, and the main window never learns about it — so its gate never appears on the phone at all. Detaching the panel after the mission started in the main window is fine.
Fix. Start missions in the main window when you intend to approve them from your phone.
The desktop is also given 4 seconds to answer a phone’s request. An overrun is reported as stalled, not failed — approving a plan starts the whole run, so the decision is applied before the answer comes back. Do not tap twice.
Remote will not start
When the server refuses to come up, Settings → Remote gives a reason. Each maps to a specific fix.
| Reason | Fix |
|---|---|
disabled | Remote is off. Nothing is listening. |
not_pro | Remote is part of a subscription. Activate or renew under Plan & license, then turn it on. |
no_tailnet_address | Private mesh is selected but no address in 100.64.0.0/10 exists. Join the mesh, or switch to Local network. Private mesh never falls back to the LAN on its own. |
no_lan_address | Local network is selected but there is no 10.x, 172.16–31.x or 192.168.x address. Join a network and turn Remote on again. |
bind_failed | Another program holds the port. Quit it and turn Remote on again. |
listener_stopped | The server stopped on its own. Toggle Remote off and on; the log has the detail. |
no_server_id | The identity your phones use to recognise this machine could not be saved — usually an unwritable app database. Remote refuses to start rather than hand out an identity that changes next launch. |
If the mode is This machine only, the server answers on 127.0.0.1 and a phone cannot connect to it directly at all. That is the mode working, not failing. See Phone remote.
Recording will not start or Stop is still cleaning up
Click DeckVoice beside Atlas to show the floating pill. Click the pill to expand its controls, then use the microphone to start and stop recording. Choose Type to turn speech into text. The gear opens quick settings for the mode and transcription engine. For the input device, choose All settings… from the gear menu. Opening the pill alone does not start the microphone.
Another Deck app owns the microphone. Stop recording or always-on listening in the named app, wait for it to finish cleaning up, then try again. A compatible DeckSpace and DeckVoice share one capture slot. If you are using an older DeckVoice build, close it first; older builds do not participate in that coordination.
Cleanup is pending. Press Stop again on the same recording. A timeout does not mean the microphone has closed, so starting another recording is refused until cleanup completes. The app reports readiness only after the audio stream starts. Changing the transcription model does not release the microphone.
Transcription failed after Stop. Retry the retained clip before making another recording. Its audio and partial transcript belong to that recording; an older result cannot consume a newer clip. A device warning may still accompany usable audio.
The hold shortcut works, but there is no audio. Shortcut permission, microphone permission and the selected input device are separate checks. Verify the device with push-button recording first, then test the physical hold key. On macOS, review System Settings → Privacy & Security; a granted screen-recording permission does not grant microphone or Accessibility access.
Linux-only failures
| Symptom | Why | Fix |
|---|---|---|
| The tray icon does not exist, and mini-mode hides the app with no way back | The tray needs a StatusNotifierWatcher on the session bus. GNOME removed its tray years ago. | Install the AppIndicator and KStatusNotifierItem Support extension. DeckSpace warns once at startup when it detects no host — and stays quiet when it cannot tell, so no warning is not proof of a tray. |
| App-owned hotkeys and dictation insert are greyed out | Wayland does not expose the app-owned key and synthetic-typing paths used on X11. A desktop portal binding is separate. | For automatic insertion use X11. On Wayland use push-button voice or a granted portal hold-to-talk shortcut; paste the transcript from the clipboard yourself. |
| Desktop hold-to-talk is not granted or stops after a portal disconnect | The portal has not granted DeckSpace its shortcut, or the session ended. A desktop name or installed interface is not a grant. | Turn Atlas on, open Voice settings → Push-to-talk and choose Retry desktop shortcut. Answer the desktop dialog, then choose Dictation or Assistant. Release the key after changing the action. Test the physical hold and microphone separately; a granted binding does not confirm either. Switching Atlas off closes the portal session. |
| Voice replies are silent | Neither paplay nor aplay is installed. | Install pulseaudio-utils or alsa-utils. The app says this once, then stops repeating it. |
| Agents write outside the workspace | bwrap is missing, so there is no sandbox backend. | Install bubblewrap and check the backend reported under Settings → Autonomy. See above. |
Activation and licence
DeckSpace requires a valid licence key and a device-bound activation token for that specific device and licence key. Both are checked offline at every launch.
| Message | Cause | Fix |
|---|---|---|
| “This license is already active on … devices” | The activation server has reached the licence’s device limit. The signed licence supplies the count; the default is two when no count is included. | Remove the licence on another device, or use the device page on deckspace.dev, then activate again. Plan & license displays the applicable device limit. |
| Renew prompt / subscription lapsed | The Stripe subscription behind the key has ended. Renewal is enforced through the activation token, not the key. | Renew, then relaunch so the app re-activates. |
| “That license key isn’t valid.” | The key was rejected outright. | Check it pasted whole — the keys are long and wrap in email. |
| “Couldn’t reach the license server.” | No network, or the store is unreachable. | Reconnect and try again. |
| Paid features locked after a long stretch offline | The activation token expires. The app tries to renew it silently once it is within 10 days of expiry — but that background attempt runs at most once per app launch, so a failed attempt is not retried until you relaunch. | Get online and relaunch DeckSpace. One successful refresh restores it. |
Smaller things
| Symptom | Cause and fix |
|---|---|
| “Can’t verify the developer” on first launch | Confirm the download came from your DeckSpace delivery link, then follow Apple’s current opening instructions. If macOS reports that the app is damaged or will harm your computer, stop and contact support; do not disable Gatekeeper. |
| A room shows an error panel instead of loading | Its code chunk failed or took more than 10 seconds. The app already retried once automatically. Press Retry; relaunching also re-fetches it. |
| “tmux not found” when opening a persistent session | Persistent sessions are tmux-backed so they survive quitting the app. Install tmux (brew install tmux). Ordinary panes do not need it. |
| An agent authenticates in one pane and not another | Panes where the CLI is the direct child have no shell in between to source your dotfiles. See the API-key note under agent CLI not detected. |
Still stuck
Preview and save a support report from Setup & diagnostics, then send the reviewed report and a short description of the failure to info@deckspace.dev. Include the steps that caused it. Inspect any additional log excerpt for private information before sharing it.