Documentation menu

Troubleshooting

Start with diagnostics#

nid doctor
nid daemon status
nid ls
nid status
nid daemon logs -n 100
nid logs <group/name> -n 100 -v

doctor reports version/install information, updates, installed agent commands, available login evidence, daemon/dashboard state and Codex sandbox readiness. Try a failing vendor CLI directly in its worktree to separate provider/account problems from nid transport problems. Log in interactively if the CLI has not been set up.

SymptomCheck or action
nid is not found after installationOpen a new terminal. Check the install directory is in user PATH; see installation.
CLI warns the daemon runs different codenid daemon restart loads the current binary/source and resumes previously running sessions.
The daemon crashed, the disk filled up, an update failed, or daemon.json is damagedSee crashes, full disks and updates: each case with its exact message.
Session says waitingInspect approvals and questions; work may need your answer.
Session says limitedInspect nid limits; see provider limits below.
Session says working quietIt has emitted no events for the configured quiet threshold; check logs and the vendor CLI.
Team stops after many automatic messagesThe 60-turn automatic-turn guard waits for a human message; send new direction.
Budget is reachednid budget <group> <usd> raises it; off removes the cap. Cost depends on vendor reporting.
Task waits in reviewAdd an eligible reviewer or use the human review commands.
PTY needs the release binary or bunRun the daemon with a release binary or Bun; the Node source runtime cannot create these PTYs.
PTY agent seems stuck in startup UIComplete trust/login dialogs and disable update prompts; keystrokes otherwise go into that dialog.
Dashboard URL refuses a saved login linkLinks are one-use and expire after 60 seconds; rerun nid dashboard.
Dashboard becomes signed out after restartExpected: the new daemon has new browser sessions; open a fresh login link.
init finds an existing configIt preserves the file by default; review it or intentionally use --force.
Daemon does not come up at loginnid daemon autostart status; re-run autostart on after moving the binary or installing agent CLIs elsewhere (it records the binary path and PATH). Linux without a systemd user session uses an XDG autostart entry, which only runs in desktop sessions. Check ~/.nid/logs/daemon.log.
A rotation failedThe rotation event and the human inbox carry the error. A failed compaction in auto mode falls back to a handoff; a session whose fresh conversation could not start is in error: nid resume it. Set rotate.mode: off to stop automatic rotations. See long-running sessions.
Agent forgets earlier work after a rotationIt starts from its handoff and the board only. Look at the handoff in the rotation event (nid logs -v); prefer rotate.mode: compact if handoffs lose too much.
uninstall changed nothingWithout a terminal it needs --yes. Data is kept unless you confirm or pass --yes --purge.

State, logs and restart#

The state root is ~/.nid, or NID_HOME. nid.db stores sessions, tasks and events; daemon.json contains the local address and credentials; logs/daemon.log contains process diagnostics; worktrees/<group> holds isolated working copies; run holds per-session runtime/MCP files. config.yaml contains user settings. Do not delete this directory to fix a routine restart: it may contain unlanded work.

nid daemon restart saves which sessions were running in restart.json, stops gracefully, starts the new daemon and resumes that set. Stopped sessions stay stopped. Rerunning an interrupted restart reuses the saved plan. It reports each failed resume and returns a failure exit code when any resume fails. If the daemon stopped without a clean shutdown (hard kill, crash, reboot, logoff, power loss), its next start kills orphaned agent processes and leaves the sessions stopped: that start is unattended, so it never spends budget on its own. The inbox lists what was running and what was mid-turn, and how to continue (see below). A daemon.lock left behind by the previous daemon is how it knows (a clean stop removes it; releases up to 0.1.0-alpha.10 never wrote one, so an upgrade is not a crash).

One unexpected agent exit after at least a minute of uptime triggers automatic resume by default; another crash within ten minutes stops it and notifies the human. Check the vendor error before repeatedly restarting it. Disable this behavior with --no-auto-restart or autoRestart: false when testing startup failures.

Retained events are bounded (default 20000 per session, pruned in batches). Older transcript entries are not available indefinitely. PTY raw terminal history has a separate small rolling buffer and disappears when the process stops.

Crashes, full disks and updates#

Each entry gives what you see, the exact message, why it happens and what to do. Messages from the daemon are in nid daemon logs; messages to you are in the inbox (nid inbox, the dashboard).

The daemon stopped without a clean shutdown#

This covers a hard kill or crash of the daemon, and the computer restarting under it (reboot, logoff, power loss, Windows shutdown with Fast Startup).

  • You see: the sessions that were running are stopped. Inbox (one message per group): The nid daemon stopped without a clean shutdown while <sessions> were running, so nid left them stopped instead of continuing them unattended. Claimed tasks, locks and messages were kept., then, for each session that was mid-turn, <session> was mid-turn, handling: with its input quoted (> lines), and Continue with: nid resume <group> --continue. When nid can tell the computer restarted, the message starts with The computer restarted instead. The lead gets a matching message when it is resumed. Daemon log: ... left <sessions> stopped (crashResume: stopped is the default).
  • Cause: the previous daemon ended without stopping its sessions and left its daemon.lock behind. The next start is unattended (autostart at login, or whatever command started it), so nid does not start turns that cost money with nobody watching.
  • What happens: orphaned agent processes are killed and the sessions are marked stopped. Nothing else runs. Claimed tasks, locks and undelivered messages are kept.
  • Fix: when you are ready, either:

    • nid resume <group> --continue: sessions that were mid-turn get one message quoting the interrupted input and pick the work up, without repeating it; or
    • nid resume <group>: the interrupted work is not picked up again. Sessions handle only messages already waiting for them; the lead, for one, has the crash note queued and takes a turn on it right away.

    To have nid continue on its own anyway, opt in with crashResume: continue (a group setting, or in ~/.nid/config.yaml for every group). Groups that opted in are resumed at the next start, and the inbox says ... was started again; resumed <sessions> (crashResume: continue). NID_CRASH_RESUME=0 overrides the opt-in.

The daemon keeps crashing#

  • Message (inbox), in groups with crashResume: continue: The nid daemon stopped without a clean shutdown while <sessions> were running, and the daemon stopped unexpectedly 3 times within 10 minutes, so nid left them stopped (check nid daemon logs).
  • Cause: something kills or crashes the daemon repeatedly. Resuming again could feed the loop.
  • Fix: read nid daemon logs -n 200, fix the cause, then nid resume <group>.

An agent crashed and stayed stopped#

  • Message (inbox): <group/name> stopped unexpectedly: <reason>. If it held tasks, its lead also gets <group/name> stopped: its agent crashed (<reason>) and was not restarted automatically. It still holds #<n> (<title>). …
  • Cause: one crash after at least a minute of uptime is resumed automatically (inbox: <group/name> crashed (<reason>) and was resumed automatically). A second crash within ten minutes, a crash right after start, or autoRestart: false stops the session instead.
  • Fix: look at the vendor error (nid logs <group/name> -v, or run the agent CLI in its worktree), then nid resume <group/name>. Its claimed tasks are still its own.

The disk is full#

  • Message (daemon log, nid status, nid daemon status): disk full at <dir>: nid could not save its state (database or disk is full). Free some disk space; nid retries automatically and resumes when it can write again. Agents whose tool calls write to the board get the same message as the tool's error.
  • Cause: the disk holding NID_HOME has no space left.
  • What happens: no new turns start. Running turns finish, but their events cannot be saved (they are counted). The database is not corrupted: SQLite rolls back each failed write. nid tries a test write every 30 s (NID_STORAGE_RETRY_MS).
  • Fix: free space on that disk. nid continues by itself and tells you in the inbox: nid could not write its database for a while, so it paused new turns and could not save some activity (<n> events). … Costs of turns that ended during the outage may be missing.

The database cannot be written#

  • Message: cannot write the nid database <file> (<error>). Check the disk and its permissions; nid retries automatically.
  • Cause: a read-only file or directory, a failing disk, or an I/O error.
  • Fix: make <file> and its directory writable for your user (or fix the disk). nid retries every 30 s and continues the same way as after a full disk.

A second daemon will not start#

  • Message (daemon log, or daemon failed to start: …): another nid daemon (pid <N>) is already running for <home>. Use it (nid daemon status), or stop it first: nid daemon stop
  • Cause: only one daemon may run per NID_HOME. The running daemon holds this home's singleton, a named pipe (\\.\pipe\nid-<hash of the home>) on Windows or a Unix socket (<home>/run/daemon.sock, or a short path under the temp directory) on macOS and Linux. The operating system closes it when that process ends, however it ends. A second daemon finds it in use, asks it, and gets the pid in the message. Two daemons on one database would drive the same worktrees twice.
  • Fix: use the running one. If you meant to restart: nid daemon restart. Nothing else can block a start: daemon.lock (whatever pid it names) and daemon.json are only records, and a socket file left by a killed daemon is reclaimed.
  • Variant: another nid daemon is already running for <home>, but it does not answer. A daemon holds the singleton but is stuck. Check nid daemon status. If it stays stuck, end the nid daemon process yourself (Task Manager, kill): nid cannot identify it safely.

daemon.json is damaged#

  • You see: with a daemon running, nothing, because it rewrites a deleted, empty or corrupt daemon.json within 5 s (log: daemon.json was missing or corrupt; rewrote it), and commands wait for that instead of starting a second daemon. With no daemon running, nid daemon status says not running (<home>/daemon.json is corrupt; the next nid command starts a daemon and replaces it) (or is empty).
  • Cause: a crash or full disk while the file was written by an older release (it is now written atomically), or something edited or truncated it.
  • Fix: none needed. (daemon.lock is only a record that a daemon is running; deleting it does not stop anything, but the next start then cannot tell that a crash happened.)

The daemon does not answer#

  • Message (nid daemon status, exit code 1): not responding: the daemon (pid <N>, version <V>) is running but does not answer on port <P>. followed by Stop it with: nid daemon stop (then nid daemon start).
  • Cause: the daemon process is alive but hung. A daemon that is only busy is not reported: nid keeps asking for 20 seconds (NID_UNRESPONSIVE_MS) before it calls a daemon unresponsive.
  • Fix: nid daemon stop. It prints stopped the daemon (pid <N>) by force: it did not answer, but only after proving the process is this home's daemon: its command line plus the port in daemon.json, or, when daemon.json is gone, this home's singleton answering with that pid. A pid named only by daemon.lock is never killed. Otherwise it reports not running (…) and leaves that process alone. Then nid daemon start and nid resume <group>. nid update never stops such a daemon by force on its own: it asks (Stop it by force and restart it? … [y/N]), or needs --force-restart; without either it prints Left the daemon (pid <N>) running. ….

After an update the daemon still runs the old version#

  • Message (nid daemon status): the daemon is running old version <X> (this nid is <Y>): restart it to update: nid daemon restart
  • Cause: nid update installed the new binary but the daemon restart failed or was skipped. On Windows the old daemon keeps running from nid.exe.old-<stamp>.
  • Fix: nid daemon restart, or run nid update again. With the binary already current it says nid <Y> is up to date. and then The daemon is still running old version <X>. Restarting the daemon and resuming its sessions.... The .old-* file is deleted the next time nid starts after the old daemon has exited.

An update failed and the binary is missing (Windows)#

  • Message:
    Update failed and nid could not put the previous binary back: <exe> is missing.
    Moving the new binary in failed: <error>
    Restoring the previous binary failed: <error>
    Recover with one of these (close other nid windows first):
      restore the previous version:  Move-Item -Force -LiteralPath "<exe>.old-…" -Destination "<exe>"
      or install the downloaded new version (checksum verified):  Move-Item -Force -LiteralPath "<exe>.download-…" -Destination "<exe>"
      (cmd.exe: move /Y "<exe>.old-…" "<exe>")
      or reinstall: irm https://nid.dev/install.ps1 | iex
    Your sessions and settings in ~/.nid are not affected.

    The command exits with code 1.

  • Cause: rare. Windows lets a running binary be renamed, so the updater moves nid.exe aside, then moves the new one in. Here both that and the way back were blocked, typically by antivirus or another program holding the file.
  • Fix: run one of the printed commands. They contain the exact paths, and both files are kept until you do. Your sessions and settings in ~/.nid are not affected.

After the laptop slept#

  • You see: sessions continue where they were. The daemon log says no timer ran for <time> (the machine was asleep or the daemon was paused); quiet, stall and idle timers continue from where they stopped.
  • Cause and behavior: sleep is not counted as silence. No "quiet" notices, stall releases or idle re-prompts fire because of it, and task budgets do not count it. Locks still expire by wall-clock time, and a vendor CLI may need a moment to reconnect.

Windows and provider-specific issues#

  • For non-yolo Codex, complete its Windows sandbox setup by running codex interactively once. doctor reports notConfigured/updateRequired until ready.
  • Windows updates move the active executable aside and clean up the old binary on a later startup. Antivirus or a second process may temporarily hold files open; inspect the updater error and close stale processes before retrying.
  • Bash examples use \ for line continuation; use one line or PowerShell backticks in PowerShell. Quote paths and goals containing spaces.
  • PTY directory links use Windows junctions. Writes through linked dependency/config directories affect the original checkout, just as with symlinks on Unix.
  • opencode may silently retry provider errors for a long time. Open its own CLI in the worktree, verify the account/model, and select a known-working model explicitly. See agent limitations.
  • A spawn fails with billing subscription: ... has no ... login or billing api: no API key: run nid auth status to see each agent's login and key, log in to the CLI (claude then /login, codex login) or store a key with nid auth set <agent> --api-key, then nid resume. nid ls shows the billing actually used (BILLING: sub, api, -). An unexpected API bill usually means a session ran with billing: api, or with auto and no subscription login; see billing.
  • When running source tests, unset NID_BIN so the test harness does not start an older installed binary. Use a separate NID_HOME for manual experiments.

For signing or notarization failures, see release troubleshooting.

Stalled tasks#

Every claimed task has a time budget: the task's own (--minutes, or budget_minutes when an agent creates it) or the group's stallMinutes (default 30). nid checks claimed tasks on every quiet-watcher tick and treats one as stalled when its owner is quiet (working with no output for NID_QUIET_MINUTES) or has held it longer than the budget (reported once per claim). Sessions waiting on you or rate-limited are never counted as stalled.

  1. Report: a board note, a message to the lead (or your inbox when there is no lead or the lead owns the task), a low-priority hint to the owner, and a stall notification.
  2. Grace: any new output from the owner within stallGraceMinutes (default 10) counts as progress and cancels it.
  3. Release: otherwise nid interrupts the owner, puts the task back to open, records the reassignment on the board and in the task (reassigned 1x), and offers it to idle teammates.
  4. No thrashing: a task already released maxReassign times (default 2) is marked blocked and escalated to you instead of being released again.
nid task add "Port the parser" --minutes 60   # per-task budget
nid task set 4 -g web --minutes ""           # back to the group default
nid group set web stallMinutes=45 stallGraceMinutes=5 maxReassign=1
nid board web                                 # claimed tasks show "12m of 30m", "reassigned 1x"

The dashboard's task cards show the same elapsed/budget and reassignment badges, and the task dialog has a time-budget field.

An agent stopped but still holds a task#

An agent whose turn ended while it still holds a claimed task is not "quiet" (nothing is running), so the stall check above would only catch it at the budget. nid handles this case on its own:

  1. After idleClaimMinutes (default 2) idle, the owner gets a message naming the task(s) and asking it to continue, mark the task done or blocked, or release it. The session's events show idle with claimed task #N: re-prompt 1 of 3.
  2. Each further re-prompt waits twice as long (2, 4, 8 minutes). Real work in between (any tool call other than board reads and status updates, or a message from you) starts the count over.
  3. After idleClaimNudges (default 3) re-prompts and another idle period, the claims are released back to open (or blocked and escalated at the maxReassign cap). The lead and your inbox get nid released #N (...) from <session>: ... stayed idle holding it after 3 reminders.

Sessions waiting on you (approval or question), rate-limited, paused by the budget, rotating or under your terminal control are not re-prompted. nid group set web idleClaimMinutes=0 turns this off.

Provider rate limits#

nid watches each agent for its provider's usage limits: Claude Code rate_limit_events and "usage limit reached" results, Codex account/rateLimits/updated notifications and usage-limit turn errors, and rate-limit (HTTP 429) prompt failures from ACP agents (opencode, kimi).

  • Near the limit (a usage window at least limitWarnPercent full, default 90): nid ls shows e.g. idle 93% used (5-hour), nid status shows near-limit:<session> 93%, and the dashboard shows a warning badge.
  • Limit hit: the session goes to state limited until the window resets. Messages for it are held (nothing is lost); you get an inbox notice. A few seconds after the reset nid resumes it automatically: the input of a turn the limit cut short is re-queued, then held messages are delivered. With an unknown reset time nid retries after 15 minutes.
  • Long waits: if the reset is more than limitReleaseMinutes away (default 20), the session's claimed task is released back to open (recorded on the board and in the task's reassignments) and offered to idle teammates, so the group keeps moving. A task already released maxReassign times (default 2, shared with stall handling) stays with its owner instead.
nid limits [group]                     # sessions near or at their limit, reset times
nid limits <group/name> --clear        # retry now (e.g. after switching accounts)

The dashboard's session page has a matching Retry now button. Set the thresholds per group in nid.yaml (limitWarnPercent, limitReleaseMinutes) or with nid group set. A limit survives nid stop/resume and nid daemon restart.