Skip to content

Troubleshooting

Symptom-first fixes for an install that is not behaving. Every section follows the same shape: what you see (with the verbatim error string, so search finds it), the most likely cause, the command to run, and a link to the page that explains the mechanism.

Start here — three commands

clauster doctor                        # config, claude binary, login, trust,
                                       # state_dir, port — explained below
curl -s http://127.0.0.1:7621/healthz  # liveness (default port 7621)
journalctl -u clauster -n 100          # server log — or: docker logs <name>

Bridge debug logs live under <state_dir>/logs/ (default ~/.clauster/logs/), one file per spawn named <label>-<timestamp>-<seq>.log. What doctor and /healthz report is described under Health checks; how to read a bridge log is in Reading the bridge debug log.

The dashboard won't load

"connection refused" / the page just times out

What you see: the browser reports ERR_CONNECTION_REFUSED or times out; curl says Connection refused.

Most likely cause: Clauster is not running, crashed at startup (see It won't start at all), or is on a different port than the one you are hitting (default 7621).

systemctl status clauster        # is it running? what did it last print?
curl -s http://127.0.0.1:7621/healthz   # from the host itself
ss -tlnp | grep 7621             # is anything listening on the port?

Mechanism: Networking — loopback vs non-loopback.

The page loads locally but not from another machine

What you see: curl http://127.0.0.1:7621/healthz works on the host, but the same port from your laptop or phone refuses to connect.

Most likely cause: Clauster binds 127.0.0.1 by default, so only the host itself can reach it. Binding a LAN address (host: 0.0.0.0) requires enforced auth — see the "refusing non-loopback host" refusal under I can't get in — or a reverse proxy in front of the loopback bind.

Mechanism: The auth / networking matrix.

The container exits immediately after docker run

What you see (in docker logs <name>): one of

exec /usr/local/bin/claude: no such file or directory
claude: not found

Most likely cause: the official image is Alpine-based (musl libc), and you bind-mounted a glibc build of claude from the host into the container. The file is present, but the glibc loader it needs is not, so exec fails with a misleading "no such file or directory".

The fix: mount a musl build of claude instead, or derive your own image that installs one (the installer needs bash):

apk add --no-cache bash curl && curl -fsSL https://claude.ai/install.sh | bash

A container can also exit immediately because of a config refusal — a non-loopback bind without enforced auth, or password_required without a hash. Check docker logs for the strings in I can't get in.

Mechanism: Installation — Docker · Networking — Docker.

I can't get in

Buttons are refused with origin check failed, or a live view won't connect

What you see: the dashboard loads and reads fine, but any action (start a session, trust a folder, restart) returns 403 {"detail": "origin check failed"}, and/or the live log / terminal view never connects (the WebSocket closes with code 1008).

Most likely cause: the browser origin you use is not in the Origin allowlist. Clauster auto-trusts 127.0.0.1/localhost/[::1] only at config.port, and only when host: is itself a loopback address. A non-loopback bind — 0.0.0.0, a LAN IP, or the Docker image (which binds 0.0.0.0) — auto-trusts nothing, so even browsing it at http://localhost:7621 is rejected. A tunnel, reverse proxy, or an SSH port-forward onto a different local port has the same effect on a loopback bind.

Fix: list the address exactly as it appears in the browser's URL bar:

auth:
  allowed_origins: ["http://localhost:7621"]   # scheme + host + port

This applies whether or not a password is set — the origin allowlist is a cross-site defence, not an authentication method, so it is enforced even when auth.enabled is false.

Mechanism: The master switch · Upgrading.

I get a 403 after putting it behind nginx / Caddy / Traefik

What you see: the proxy reaches Clauster, but requests are denied with 403.

Most likely cause: trusted-header (forward-auth) mode is misconfigured — the request does not arrive from an IP in the trusted-proxy list, or the identity header the proxy is supposed to inject is missing or stripped. Test directly against the Clauster port (bypassing the proxy) to isolate which side is denying.

Mechanism: Behind a reverse proxy · Trusted-header (forward-auth) mode.

What you see: the password is accepted but you land back on the login page; every click bounces you to /login.

Most likely cause: the signed session cookie never makes it back — a proxy stripping Cookie/Set-Cookie, the browser blocking the cookie, or mixing origins (reaching the same instance via both http:// and https:// or via two hostnames). Try a private-browsing window directly against the Clauster port; if that works, the proxy layer is eating the cookie.

Mechanism: Behind a reverse proxy · Security — sessions and cookies.

"password_required but no password_hash set"

What you see: clauster doctor fails the auth row with password_required but no password_hash set, and startup refuses with:

auth.password_required is set but auth.password_hash is empty. Generate one
with `clauster hash-password` (or set CLAUSTER_AUTH_PASSWORD_HASH).

Most likely cause: password login is enabled but no hash is configured. Clauster fails closed rather than serving a login form nobody can pass.

clauster hash-password    # prints the hash to paste into auth.password_hash

Mechanism: Security — passwords · auth config reference.

"refusing non-loopback host ... without enforced auth" — it refuses to start

What you see: startup aborts (or doctor fails the auth row with non-loopback host <x> without enforced auth):

refusing non-loopback host='0.0.0.0' without enforced auth. Set auth.enabled:
true together with auth.password_required (+ a hash from `clauster
hash-password`), auth.reverse_proxy.enabled, or auth.api_token_hash (from
`clauster hash-token`) — or, to opt out on a trusted LAN,
auth.allow_unauthenticated_network.

Most likely cause: you changed host to a LAN/all-interfaces address without configuring an auth method — deliberate fail-closed behavior, since an open dashboard on a network port hands out control of your machine. Follow the message: enable one of the listed auth methods, or explicitly opt out with auth.allow_unauthenticated_network on a trusted network.

Mechanism: Authentication (fail-closed) · Password auth on a non-loopback bind.

It won't start at all

"address already in use" — port already taken

What you see: startup dies with an OS-level address already in use error; clauster doctor warns on the port row: 7621 already in use (Clauster already running?).

Most likely cause: another Clauster (often a still-running service unit) or an unrelated process owns the port.

ss -tlnp | grep 7621     # who owns it?

Stop the other process, or change port in clauster.yml.

"not writable" / permission denied writing state

What you see: doctor fails the state_dir row with <state_dir> not writable: ... or <state_dir> can't be created: <ancestor> not writable.

Most likely cause: the user running Clauster (often a dedicated service user under systemd) does not own the state directory (default ~/.clauster).

ls -ld ~/.clauster       # as the service user — who owns it?

Fix ownership, or point state_dir somewhere the service user can write.

Mechanism: Run as a systemd service.

"no config found" / "invalid config"

What you see: doctor fails the config row with one of four details — no config found: ..., config unreadable: ..., invalid config: ..., or config is not valid YAML ... — and most other verbs exit 2 with a clauster: config error: message. That message carries the raw parser or validation error, except for the two fixed verdicts in the table below. Three verbs behave differently: doctor itself exits 1 with the table above, clauster run with no config at all opens the setup wizard rather than failing, and install-service still renders a unit using defaults.

Most likely cause: no clauster.yml where Clauster looks for one, or a mistake inside it. The four details separate the cases, which is what tells you where to look. config is not valid YAML covers two rows below. One row carries a position. The other states a fixed verdict:

Detail What is wrong Where the message points
no config found No file at any searched path The search order
config unreadable The file exists but cannot be read — most often a permission problem, such as a root-owned file under a service user The error's class and the path. Fix the file's permissions, not its contents
config is not valid YAML (...) The file does not parse — a stray tab, an unclosed quote The error's class, plus a line and column. When the parser knows where the construct opened, a second position follows. A control character gives a character offset instead
config is not valid YAML: ... A value no type can be built from (a: 2020-13-45), or a file nested too deep A fixed sentence, with no position. The rejected value is withheld (see below)
invalid config The file parses but a value is rejected One key.path: reason entry per rejected key — or, for a root that is not a mapping, the root's type and the file
clauster doctor -c /path/to/clauster.yml   # same file the service uses

The detail points at the fault. It never echoes the offending source line, and never the whole parsed mapping. The dashboard serves these same rows over /api/doctor. A clauster.yml whose auth.token line is the broken one would otherwise put that token in the browser. That is also why a: 2020-13-45 reports a fixed sentence, not "month must be in 1..12". The same code path produces invalid literal for int() with base 10: '<your token>', and nothing there can tell the two apart. A validator that rejects a path still names that path. Open the file at the reported line to read the offending text.

⚠️ The guarantee covers the doctor detail, which the browser can reach. It does not cover the CLI. clauster: config error: goes to the terminal and the service log of the host that holds the file, and it still quotes the offending line and the parsed mapping.

Mechanism: Configuration — loading & overrides.

"database migration failed; refusing to start"

What you see (server log):

database migration to head failed; refusing to start: ...

followed by a database migration failed: ... error.

Most likely cause: the state database is ahead of the binary (it was migrated by a newer build and you downgraded), or the file is corrupted. Clauster refuses to run against a schema it does not understand rather than guessing.

The fix: upgrade back to (at least) the version that migrated the DB, or restore a backup — see Recovering from a corrupted state database and upgrading.md.

A bridge won't start, or dies right after starting

The card sits on "starting" then goes to "error"

What you see: the project card shows starting, then flips to error.

Most likely cause: the bridge spawned but never registered an environment within startup_grace_seconds — most often a claude login problem (the bridge starts, then hangs unauthenticated).

clauster doctor                          # read the claude-login row
tail ~/.clauster/logs/<label>-*.log      # the bridge's own account of it

Mechanism: Reading the bridge debug log · Bridge lifecycle.

The card goes straight to "crashed"

What you see: the card shows crashed shortly after spawn.

Most likely cause: the claude process exited unexpectedly. The bridge logs its failure reason to its debug file before exiting, and a spawn that fails outright also captures a stderr tail — so the card's log view (or the on-disk log) usually names the cause.

"spawn of ... failed to launch"

What you see (server log): spawn of <name> failed to launch: ... (or pty spawn of <name> failed to launch: ... for an Interactive Session).

Most likely cause: the claude process could not be started at all — a bad binary path, a permissions problem, or a PATH issue (next section). The tail of the message is the underlying OS error; start there.

"claude binary 'claude' not found on PATH; Clauster cannot start."

What you see: exactly that string, from clauster run or doctor's claude row.

Most likely cause: Clauster runs in an environment whose PATH does not include claude — classically a systemd unit (minimal PATH) with claude installed per-user or via nvm-managed node.

The fix, in order of preference:

  1. Set claude.binary in clauster.yml to the absolute path of the binary.
  2. Add its directory to the service unit's PATH (see the systemd notes in Installation).
  3. For nvm-managed node tooling in spawned bridges, see claude.path_append and claude.node_from_nvm in the claude config reference and npx/node MCP servers under systemd.

The trust prompt keeps blocking the spawn

What you see: a bridge spawns but sits waiting on Claude's workspace-trust prompt instead of becoming ready.

Most likely cause: the git repository needs its own trust grant. Since Claude Code 2.1.232 a parent directory's grant no longer covers nested git repos, so trusting projects_root does not trust the repos under it (a non-repo directory still inherits from a trusted ancestor).

Fix: launch it once via Trust & start (which trusts, then spawns), or click Trust all on the dashboard to grant every discovered project its own key at once.

Mechanism: Security — workspace trust.

"~/.claude.json exists but is not a valid JSON object"

What you see: Trust, Trust all, Trust & start, clauster start --trust or a background-session dispatch fails with this message:

could not update trust state: /home/you/.claude.json exists but is not a valid JSON
object (invalid JSON at line 1 column 58). Clauster did not change it. Repair the file
or restore it from a backup, then try again.

A config-write save of an MCP server to user or local scope fails with the same text. A start without Trust fails with directory not trusted, because Clauster reads a file it cannot parse as "nothing is trusted".

Cause: the ~/.claude.json of the user that runs Clauster exists but does not parse. For example, a crash in the middle of a write can truncate it. The text in parentheses names the failure. It gives a line and column for invalid JSON, or says it is not UTF-8 text, it is nested too deeply to parse, or the top level is not a JSON object. An empty file also fails. The message never quotes the file content.

Clauster does not write a file that it cannot parse. A write would keep only the keys that Clauster sets, and remove your other projects, trust grants and account data. The file stays byte-identical.

Fix:

  1. On the Clauster host, run this command as the user that runs Clauster. If the file has a problem, the last line of the output names it. For invalid JSON, that line gives a line and column:
python3 -c 'import json, sys; d = json.load(open(sys.argv[1], encoding="utf-8")); sys.exit(None if isinstance(d, dict) else "the top level is not a JSON object")' ~/.claude.json
  1. Repair the file, or restore it from a copy that you trust. If Clauster wrote the file before, ~/.claude.json.bak holds its content from before the first Clauster write. That copy can be old, so compare it with the damaged file before you use it.
  2. Run the command from step 1 again. No output means that Clauster can read the file.
  3. Do the action again.

Claude itself is the problem

"not logged in" / "access token has expired; re-authenticate with claude"

What you see: doctor warns on the claude-login row — not logged in (... missing) — run `claude` to authenticate; bridges inherit the operator's login or logged out (no access token in ...) — re-run `claude` — or an operation fails with access token has expired; re-authenticate with `claude` or no claudeAiOauth.accessToken in ....

A bridge that starts without a login fails at once. A standard bridge card shows Error: You must be logged in to use Remote Control. An Interactive Session card shows "Session failed to start" with claude is not logged in on this host, or its first-run setup is not finished. For an Interactive Session, claude does not exit. It opens its first-run screens and waits for a keypress. The keeper reads those screens at the start of the session output, records the error, and stops the bridge about 5 seconds later. claude can also show these screens when its first-run setup was never finished on the host. In both cases, run claude interactively once as the service user.

Most likely cause: the account that runs Clauster has no usable claude credentials. Bridges inherit the operator's login, so a bridge cannot connect without one.

The fix: run claude interactively as the service user and complete login, or use the dashboard login flow (login_shepherd). An ANTHROPIC_API_KEY in the service environment also satisfies the doctor check.

"a login flow is already in progress; cancel it first"

What you see: the dashboard sign-in panel refuses to start, reporting that a login flow is already in progress.

Most likely cause: an earlier sign-in was started but its authorize link was never used. The shepherd runs one flow at a time, so the abandoned one holds the slot.

The fix: reload the dashboard. The sign-in panel reads the server's state on load, so it re-opens on the in-progress flow with its authorize link and a Cancel button — cancel, then start again. A flow untouched for more than 15 minutes is also reclaimed automatically by the next sign-in attempt.

The claude version is too old (min_version)

What you see: doctor fails the claude row with <version> < required <min_version> — run `claude update` (or reinstall via the installer) to reach >= <min_version>.

The fix: claude update (as the same user Clauster runs as), then re-run clauster doctor.

If the row starts with cannot compare: instead, doctor cannot confirm the floor, so it fails the row. The message names the value that is not a version:

  • min_version '<value>' is not a numeric version: set claude.min_version in clauster.yml to a version such as 2.1.145. A single number such as 2 is also accepted and means 2.0.0.
  • `claude --version` reported '<value>': the installed claude printed something that is not a version, for example a bare commit SHA. Reinstall claude with the installer.

In a session, Node fails with GLIBC_2.38 not found, or clauster cannot load libpython

What you see: this applies to the standalone binary. On Linux, in a session that Clauster started, node, npm or a Node-based MCP server fails with a GLIBC_2.38 not found error for libstdc++. On any OS, clauster (or the recap hook) run from such a session fails to start, for example because it cannot load libpython3.13.so.1.0. On Linux, clauster doctor shows a bundle-env warning that names the process.

Most likely cause: an older Clauster release started the bridge, the background agent or the claustrum daemon. Older releases passed the binary's own runtime environment to every process they started: its _MEI… unpack directory in LD_LIBRARY_PATH, and the _PYI_* variables. The programs in the session then load the binary's bundled libraries, or look for an unpack directory that a Clauster restart already deleted. Current releases remove these variables before they start a process.

The fix: upgrade Clauster first. A process keeps the environment it started with, and bridges, pty keepers, background agents and the claustrum daemon all outlive a Clauster restart. So an upgrade alone does not change them. Restart each old one:

  1. For a standard bridge, click Stop on its dashboard card, then click Resume.
  2. For an Interactive Session (pty) bridge, click Stop on its card, then click Resume. Stop also ends the pty keeper that holds the bridge, and Resume starts a new keeper.
  3. For a background agent, wait until it finishes its task, then click Resume. If it waits for your input or a permission answer, answer it first. Resume starts a new background job, with a new id, that continues the conversation. If its row shows no Resume, the agent has no recorded session, and you cannot resume it. Do not click Stop first: Stop removes the agent from the list, and you cannot resume it after that. Claude Code starts background agents from its own supervisor process, which Clauster does not manage. If new agents still get the warning, the likely cause is that the supervisor has the old environment. End every background agent so that the supervisor can exit, then start the agents again.
  4. For Direct Sessions, end each session, then stop the claustrum daemon process. Its command line contains -serve -socket <state_dir>/claustrum/daemon.sock (or the claustrum.socket_path that you configured). Then restart Clauster, which starts a new daemon at startup. Every Direct Session gets its environment from the daemon.

To see if one process still has the old environment, run this on the Linux host. No output means that the process is clean:

tr '\0' '\n' < /proc/<pid>/environ | grep -E '^(LD_LIBRARY_PATH=(.*:)?[^:]*/_MEI|_PYI_)'

Do not run this check on a pty keeper's own pid. In the binary, a current keeper always has these variables: on Linux and macOS it runs as its own copy of the binary, with its own _MEI… directory. It removes them from the bridge that it starts. Check the bridge pid instead.

An Interactive Session (pty) bridge is wedged

The live log tail is silent or frozen

What you see: the dashboard's live log view stops updating.

Most likely cause: the WebSocket stream died, not the bridge. Check whether the on-disk log is still growing:

tail -f ~/.clauster/logs/<label>-*.log

If it grows, the bridge is fine — reload the dashboard to reconnect the stream. If it is silent too, treat it as a wedged bridge: stop and resume it from the card.

The bridge survived a restart and I can't stop it

What you see: after a Clauster restart there is a claude process (and its keeper) still running with no project card that controls it.

Most likely cause: pty (Interactive Session) bridges are designed to survive a systemctl restart under the shipped unit's KillMode=process; normally Clauster reattaches to them at startup. One that was orphaned instead is cleaned up with:

clauster keepers                      # list orphaned pty keepers (no project card)
clauster keepers --kill <pid>         # stop one by its keeper PID
clauster keepers --kill <pid> --force # stop a keeper still on a project card (recovery path)

Mechanism: clauster keepers · the KillMode / restart caveat.

Is the bridge dead, or is the stream dead? — how to tell

Three probes, from cheapest to most direct:

  1. On-disk log growing? (tail -f as above) — growing means the bridge is alive and only the dashboard stream is stale; reload the page.
  2. Card status — crashed/error mean the bridge is gone (the status table is in Reading the bridge debug log).
  3. Process there? pgrep -a -f 'claude.*remote-control' — a live process with a dead card usually means an orphaned keeper (previous section).

What you see: a green Running Interactive Session card with no Open in Claude button and no QR button, carrying that sentence where the buttons would be.

The bridge is fine. Only its deep link is missing. The session runs, the live log tail works, and Stop and the live terminal all behave normally.

Cause: the keeper reads the connect link off a terminal screen it rebuilds from the bridge's output. Three faults stop it:

  1. A rare byte sequence, a double-width character left half-overwritten, makes one frame fail to render. The keeper skips the bad chunk and retries on the next one, which normally succeeds within the 30-second capture window.
  2. The screen emulator rejects an escape sequence before the link is found. The emulator skips the rest of that chunk. If the link was in that chunk and the bridge does not print it again, the link is lost. The screen and the live terminal keep working.
  3. A failure disables the screen emulator for the rest of the session.

This message means capture never succeeded. If fault 2 is the only fault, the card instead says "Connect link unavailable — part of this session's output was skipped by the terminal emulator." The cause and the fix are the same.

Where it happens: Windows always, and Linux or macOS only with claude.pty_screen_enabled: true. The keeper builds that screen unconditionally on Windows, because a Windows console fragments the link across the raw byte stream. Elsewhere it builds one only for the live-terminal view, and without a screen there is no screen fault to report.

The fix: open the session from claude.ai/code and pick it from your session list, or Stop the card and start a new session. Nothing needs repairing on the host.

Mechanism: the keeper writes the reason into its sidecar next to the bridge's log, and Clauster lifts it onto the card. The exact fault is also logged on a line that begins with clauster.pty_keeper:. The three faults log screen could not be rendered, rejected an escape sequence (once for each error type), and failed and was disabled:

grep clauster.pty_keeper ~/.clauster/logs/<label>-*.keeper.log

Everything looks fine but the session never responds

Hosted / Direct Session: "cannot connect to claustrum"

What you see: a Direct Session fails with cannot connect to claustrum at <state_dir>/claustrum/daemon.sock, or the API returns hosted channel unavailable: claustrum daemon not connected.

Most likely cause: the claustrum daemon is not running (or not installed — it is an optional dependency), or its socket path is wrong.

curl -s http://127.0.0.1:7621/healthz      # the claustrum block names the reason
clauster deps list                         # is claustrum installed?
tail ~/.clauster/claustrum/daemon.log      # what did the daemon last say?

Start with /healthz: its claustrum block carries an error describing why the last start attempt failed — including the commonest case, the binary not being found.

Authenticate that request on a deploy with auth.enabled — an unauthenticated caller gets liveness only, with no claustrum block at all. The block is also absent when claustrum.enabled is false. See Health checks for what /healthz returns to whom.

Mechanism: claustrum config reference.

"claustrum daemon unavailable at startup"

What you see (server log): claustrum daemon unavailable at startup: ... — Clauster keeps running, but Direct Sessions are unavailable. Related messages: running claustrum daemon rejected the persisted token and claustrum daemon connection lost.

Most likely cause: the daemon failed to start or an old daemon is running with a token Clauster no longer holds. /healthz reflects claustrum health; restarting Clauster retries the connect-or-spawn cycle.

Reattach failed after a restart

What you see: after a Clauster restart, a previously live Direct Session card shows an error like reattach failed: ... (server log: hosted: reattach of <id> failed: ...).

Most likely cause: the underlying daemon process ended while Clauster was down, so there was nothing to reattach to. The transcript is not lost — start a new Direct Session for the project and resume from the captured session.

"stream lost: internal error (...)" on a Direct Session

What you see: a Direct Session card shows the red Error badge, and its view shows stream lost: internal error (<ExceptionName>) (server log: hosted: pump for process <id> failed, followed by a traceback).

Most likely cause: a bug in Clauster. Clauster could not process one line of the session output and stopped reading that session. The view does not show the exception message, because it can contain session output. The server log has the full traceback. Its formatter masks known token and id shapes, but the message can still quote session output. Please report the traceback as a bug.

Clauster then ends the agent at once with a forced kill (SIGKILL on Linux and macOS; claustrum ends the job object on Windows), with no grace period, because nothing can read its output or answer its prompts any more. A tool command that the agent runs at that time also stops. An open permission prompt shows — session ended, and Clauster sends no answer for it to the agent. The transcript of claude keeps the conversation up to the last line that claude wrote before the kill. If the card offers Resume, use it to continue the conversation in a new agent.

A stream lost: line with any other text than internal error (...) means that the claustrum daemon refused a request for the session, or that Clauster lost its connection to the daemon. Clauster handles the session the same way: it ends the agent and resolves open prompts. If Clauster cannot confirm the kill, the server log shows hosted: could not end agent <id> after pump failure. That can mean that the connection was gone and the kill failed, or that the daemon received the kill but its reply did not arrive. Before you resume such a session, make sure that its claude process is not still running on the host.

"output lost (N frames)" after a restart

What you see: a reattached Direct Session's view opens with a yellow output lost (N frames) marker ahead of the replayed output (server log: hosted: daemon replay buffer evicted frames <first>-<last> for process <id>).

Most likely cause: the agent produced more output while Clauster was down than the claustrum daemon's per-process replay buffer holds, so the daemon dropped the oldest frames before Clauster could read them. The marker is deliberate — those frames are gone from the daemon and Clauster will not pretend the stream is continuous. The conversation itself is unaffected: the session keeps running and claude has still written its own transcript to disk. Restarting Clauster promptly, or keeping sessions shorter, narrows the window. The same marker can show after Clauster reconnected to the daemon while it kept running: see When the daemon connection drops.

A Direct Session shows "claustrum connection dropped" or "the reconnect failed"

What you see: a Direct Session card shows the red Error badge with one of these texts. The server log has a hosted: session <id> marked lost: ... line for the session. If the daemon closed the connection, an earlier line says claustrum: the daemon closed the connection; reconnecting.

  • claustrum connection dropped and the daemon no longer has this session
  • claustrum connection lost; reattach failed: ...
  • claustrum daemon connection lost and the reconnect failed: ...

Most likely cause: the connection to the claustrum daemon ended, and Clauster could not move the session to a new connection. In the first case the daemon reconnected but no longer had the process: the process ended, or the daemon restarted and lost it. In the other two cases the daemon did not answer, or Clauster could not reach or start a daemon. Read daemon.log in the claustrum/ directory under state_dir for the reason that the daemon gives.

A card with the orange orphan badge instead means that the claude process still runs, but Clauster could not reattach it. Its text says still running, but Clauster could not reattach it. The cause can be a daemon restart, a dropped connection or a request that timed out.

If only the connection was lost, Clauster reattaches the session again over the next daemon connection. If the daemon still has the process, the card shows Running again. After the reconnect failed, that next connection comes only when you start a new Direct Session, or use Resume or Restart on a Direct Session. See When the daemon connection drops for each case.

The transcript is not lost. If the card offers Resume, use it to continue the conversation in a new agent. If it offers Kill, the old claude process is still running: kill it first. Resume on an Error card first ends the old agent through the daemon. If the daemon refuses or does not answer that kill, Resume refuses with cannot resume: the old agent may still be running. Read daemon.log for the reason, and try again. Forget on an Error card does the same. It refuses with cannot forget: the agent may still be running, and the claustrum daemon cannot be reached to end it when Clauster cannot connect, and with cannot forget: the old agent may still be running, and the daemon refused or did not answer the kill when the daemon does not take the kill. A Stop or Kill of an Error row through the API refuses the same way, with cannot stop or cannot kill. With claustrum.enabled: false the text ends no claustrum daemon is connected to end it. After the daemon kill, Resume, Forget, Stop and Kill also end the agent by its process id when Clauster has one, so an agent that outlived a daemon restart ends too.

A Direct Session names a lost permission prompt

What you see: after a Clauster restart or a dropped daemon connection, a Direct Session card shows an error that ends with the agent was waiting on a permission prompt that Clauster could not restore, so it cannot be answered here: Bash (request ...).

Most likely cause: the agent was waiting on that prompt when Clauster stopped, and the reattach did not find the agent running. The daemon no longer had the process, it refused the reattach, or the connection dropped. If the card offers Kill, the claude process may still run and wait on the prompt: kill it, then Resume the conversation. After a dropped connection, the next daemon connection reattaches the session and opens the prompt again. On an Error card whose agent may still run, or an orphan card, the text comes back after each restart until you Resume, Kill or Forget the card. The same text can show after a reconnect, with no restart. On a Crashed card with no orphan badge, or a card that reads claustrum connection dropped and the daemon no longer has this session with no orphan badge, the agent is gone, so the text shows once. See A Direct Session's view after a restart.

Getting more detail

Raising the log level

New in 1.1: the log_level key and its CLAUSTER_LOG_LEVEL override. Before that the server logged at info in every deployment.

Set log_level (default info) to raise the server's own verbosity — it covers Clauster's loggers and uvicorn's alike:

log_level: debug   # debug | info | warning | error

For a one-shot run, or in a container or unit file, use the env override instead of editing the config:

CLAUSTER_LOG_LEVEL=debug clauster run -c clauster.yml

Either way the level is read once at startup, so restart Clauster after changing it. What debug mostly adds is uvicorn's own debug records — per-request and per-connection detail — plus a handful of Clauster's best-effort cleanup diagnostics (a stop signal that was a no-op, a worktree unlock that failed) and claustrum daemon lifecycle notes. It is a wider view of the same events, not a separate decision trace.

Treat it as a diagnostic setting, not a steady state: it is high-volume, and although every line still goes through the same redaction as info (see log redaction), it records much more about what the server is doing — so treat a debug log as sensitive and skim it before attaching it to an issue.

Note the logs.* config keys are a different thing entirely: they control the bridge debug log stream (rotation, ANSI stripping, redaction), not server verbosity.

Reading the bridge debug log (on-disk vs the redacted stream)

The dashboard's live tail is ANSI-stripped and redacted; the on-disk file under <state_dir>/logs/ is, by default, the verbatim debug log (logs.redact_session_url: false — redaction happens only over the WebSocket). Full walkthrough: Reading the bridge debug log · logs config reference · how redaction works.

What clauster doctor checks actually mean

Doctor's output has two kinds of rows. The table lists the core rows, present in every run:

Row OK means A warn/fail usually means
config clauster.yml found and valid no config found: ... / config unreadable: ... / config is not valid YAML ... / invalid config: ... (the "no config found" section above breaks these down)
claude binary found, version ≥ min_version not on PATH, too old, or a version that cannot be compared — see above
claude-login usable claude credentials not logged in — bridges fail to start
projects_root the directory exists wrong path in config
git git on PATH create/clone features unavailable
state_dir writable permissions — see above
auth configuration consistent one of the two auth refusals above
port port free already in use (Clauster already running?)
version build up to date upstream has a newer release / stale checkout
systemd KillMode=process (bridges survive a restart) a restart would kill live pty bridges
node-toolchain node resolvable for npx MCP servers nvm-only node invisible to bridges
workspace-trust N/M discovered projects trusted (OK when all are) any untrusted discovered project — a session there will stall on the trust prompt (see above)
bundle-env no running bridge, background agent or claustrum daemon has an older binary's environment (Linux only) a process that an older standalone binary started; restart it as described above. could not be read or check not completed means doctor could not read that process's environment, so it does not know. Check the process with the /proc command, run as the user that owns the process (or with sudo)

The rest belong to two prefixed families that grow as optional pieces are added, so read the prefix rather than expecting this page to enumerate them:

  • extra:<name> — an optional Python extra. Today: extra:pyte (live terminal view), extra:apprise (outbound notifications), and on Windows extra:pywinpty (Interactive Sessions). A warn carries the exact install hint (e.g. pip install 'clauster[notify]'). apprise is shown only when notifications will actually send — notifications.enabled with at least one notifications.urls entry (#1016); with the channel off or no URL configured, runtime never imports apprise, so it isn't a nag worth carrying here. pyte and pywinpty are always shown when missing: pyte also reassembles the bridge connect-URL and pywinpty is the Windows Interactive-Session backend, so they matter beyond the opt-in live-terminal view.
  • binary:<name> — a bundled companion binary managed by clauster deps. Today: binary:claustrum (Direct Session daemon, shown when claustrum.enabled) and on Windows binary:shawl (service wrapper). A warn shows the clauster deps install <name> remedy. Presence is resolved the same way the daemon spawns, so a configured claustrum.binary path (the minimal-PATH workaround) counts as present rather than warning falsely. For claustrum the row also reports the detected version from claustrum --version and warns (advisorily — never a doctor failure) when it can't be confirmed at or above the release clauster pins. A go install-built binary keeps the unstamped claustrum-dev sentinel, and a local build can report a bare commit SHA. Neither is a version, so when --version can't confirm the floor doctor reads the module version Go embeds in every binary and names the release outright — claustrum v1.3.1 < required v1.7.1 rather than a can't-tell shrug. That fallback stays quiet unless it is certain: a build info blob that is absent, unreadable, records (devel), or belongs to some other Go program leaves the original advisory in place. A separate binary:<name>:shadow warn appears when a managed deps/bin install exists but a different PATH/configured binary wins resolution, so you know which copy actually runs.

Family rows appear only when applicable on your platform and config, and they warn, never fail: a missing optional piece leaves a feature dormant without flipping doctor's exit code. clauster deps list shows the same inventory with install state.

Still stuck — filing a useful issue

What to attach

  • clauster --version and your OS / install method (pip, binary, Docker...).
  • Full clauster doctor output — it is designed to be safe to share.
  • The relevant server-log excerpt (journalctl -u clauster / docker logs). If info doesn't show the cause, reproduce with log_level: debug and attach that excerpt instead — scrubbed, as below.
  • For bridge problems: the tail of the bridge's <label>-<timestamp>-<seq>.log — scrubbed first, see below.

The issue tracker is the support channel — see Support.

Scrubbing logs before you post them

The on-disk bridge log is not redacted by default

The redaction you see in the dashboard applies to the live stream only — with the default logs.redact_session_url: false, the on-disk log under <state_dir>/logs/ is verbatim, including session URLs that act as bearer-equivalent credentials. Set logs.redact_session_url: true and reproduce if possible, then review the log and scrub any remaining session URLs or secrets before sharing it — even with redaction enabled, the redactor recognizes only the ids Clauster learned and known token shapes.

Mechanism: Log redaction.