Operator-facing recovery for the most common failure modes. Each entry names the symptom, the most likely cause, and the first thing to try.
Read Monitoring for the canonical
status/history/events.jsonlwalkthrough. This page is the shortlist.
If a run has produced no log output for run_idle_timeout seconds (default: 3600), the heartbeat watchdog aborts it and the run is emitted as aborted with a run.idle_timeout event written just before.
- Check
sandman statusfor elapsed time. - Read the row's log (
.sandman/batches/<batch-id>/runs/<run-id>/run.logor via the portal's Log tab). - If the agent is genuinely doing real work but is silent for long stretches (for example, waiting on an external webhook), disable the watchdog for that invocation:
sandman run --run-idle-timeout 0 <issue>.
See Monitoring > Idle timeout.
A BlockedBy relationship refers to an issue that is not in the current batch. Two options:
- Add the blocker explicitly to the batch:
sandman run <this-issue> <blocker>. - Auto-expand with transitive blockers:
sandman run --include-dependencies <this-issue>.
Cycles are reported with the cycle path. See Workflows > BlockedBy-aware execution.
A worktree whose HEAD points to a different branch than its directory name expects — typically caused by a previous run that was interrupted after git worktree add but before the agent checked out the right branch.
- Detect:
sandman stranded [--json]. The text output prints a one-linegit checkout -f ...remediation per worktree.--jsonreturns the structured list. - Auto-recover:
--reconcile-stranded(enabled by default onsandman run --overrideand on a fresh issue under--continue) prunes and re-registers the stale worktree. Opt out with--no-reconcile-stranded. - Prunable worktrees (the gitlink in
git worktree listis missing) are also auto-recovered on--continue.
See sandman stranded.
Sandman run fails fast with a clear error when the .sandman/Dockerfile is missing in container mode. The preflight check is covered by TestRunBatch_ContainerModeFailsBeforeAgentWhenDockerfileMissing in the unit suite.
- Confirm
.sandman/Dockerfileexists. If not, re-runsandman initto scaffold it. - Confirm the chosen runtime (
podmanordocker) is onPATHand the sandbox user has permission to run containers. - Confirm
keychain_auth: falseon the active agent preset. Keychain auth is explicitly rejected in container mode — see Agent Compatibility > Container auth model.
Sandman does not migrate on-disk state across version upgrades. Existing .sandman/ state can contain identifiers the current portal does not understand.
- Clear
.sandman/and rebuild:rm -rf .sandman && sandman init. - Re-run current Sandman jobs after re-initializing.
The review daemon's primary defence is the daemon-side redactor. The agent's decision.md body cannot contain a /sandman substring after the daemon transforms it. The redactor runs out-of-band of the LLM, so the trigger substring is stripped regardless of what the model writes.
- Confirm
sandman reviewis running. The command starts the review daemon and does not accept positional arguments. - If a bot review body does land with
## Previous review progressand the literal/sandman reviewsubstring, the structural sniffLooksLikeBotReviewBodydrops it beforeParseTriggerruns — defence-in-depth. - The daemon-side redactor is the primary defence; the structural sniff is defence-in-depth.
Sandman shells out to gh for every issue fetch, PR check, and review comment.
- Run
gh auth status. Confirms scopes (repois required for issue reads and PR writes on private repos). - For e2e tests, the
ghshim contract is documented in Testing > GH shim contract; a shim must include theblocked_byfield on the issue JSON, not just body text, for dependency detection.
Interrupted or failed e2e runs leave worktrees, orphaned batch directories, and temp directories under /tmp/. The accumulation is most painful in CI with disk quotas and in worktree-based sandboxes.
- Preview:
sandman clean --dry-run --orphaned. - Full cleanup:
sandman clean --all— runs stale recovery, orphaned removal, archived removal, and the shared temp-dir /sandman-smoke-*image sweep in one pass. - Remove orphaned test batch dirs:
sandman clean --orphaned. - Recover stale runs in dead batches:
sandman clean --stale— emitsrun.abortedevents so the event log matches the on-disk state. - Bare
sandman cleanis a hard error: every invocation needs an explicit mode flag (--all,--archived,--stale, or--orphaned). - Combinations and mutual exclusion rules are documented in Commands >
sandman cleanand Testing > Side effects and cleanup.
127.0.0.1 is the default bind host. If you started the portal expecting to reach it from another machine:
- Use
sandman portal --host 0.0.0.0(or setSANDMAN_PORTAL_HOST=0.0.0.0). - Confirm any firewall allows the chosen port (default 5000).
- See Portal > Expose the portal on another interface.
Smoke tests skip the expensive real-agent cases unless SANDMAN_RUN_SMOKE_E2E=1 is set. When enabled, they build a per-provider / per-buildTools image on demand. Set SANDMAN_SMOKE_PREFETCH=1 to enable the optional upfront prewarm fan-out; subsequent test invocations reuse the cached image unless the cache is cleared.
- Enable the real-agent smoke path with prewarm:
SANDMAN_RUN_SMOKE_E2E=1 SANDMAN_SMOKE_PREFETCH=1 SANDMAN_TEST_PROVIDERS=opencode go test -tags smoke ./internal/cmd -run Smoke. - See Testing > Smoke image prewarm.
Sandman resolves user.name and user.email from ~/.gitconfig, then the host global/XDG Git config, then repo-local .git/config. sandman run fails early if either value is missing.
- Set the identity before the first run:
git config --global user.name "..."andgit config --global user.email "...". - Sandman never stores its own commit identity; the agent commits under your identity.