Problem
After a Tower recovery event (machine crash, tower start after downtime, network partition), some architect terminals enter a crash-loop displaying repeated "Could not resume session ..." errors. Users cannot cancel or dismiss the errors — the loop continues until Tower is externally killed or the offending row is manually removed from ~/.agent-farm/global.db.
Reported by users 2026-07-08.
Root cause (from subagent investigation 2026-07-08)
Both reconcile paths pre-compute restartOptions.args from the stored session id:
packages/codev/src/agent-farm/servers/tower-terminals.ts:661-690 (Phase 1 startup reconcile)
packages/codev/src/agent-farm/servers/tower-terminals.ts:898-925 (on-the-fly reconnect)
These args are handed to SessionManager.setupAutoRestart (packages/codev/src/terminal/session-manager.ts:845-891), which replays session.options.args verbatim after restartDelay (2s), up to maxRestarts (50 — tower-instances.ts:532).
If --resume <uuid> is unresumable (jsonl deleted, corrupted, or foreign), Claude Code prints Could not resume session ... and exits immediately. The exit re-triggers auto-restart with the same args. startRestartResetTimer fires unconditionally after each spawn() call (not after a "healthy" signal), so a fast-crashing process prevents the counter from ever resetting — count climbs monotonically to 50 (~100 seconds of errors) before removeDeadSession finally halts the loop.
No escape hatch exists — no env var, no CLI flag, no Tower HTTP endpoint. User must kill Tower or hand-edit global.db to break out.
Load-bearing context: the comment at packages/codev/src/agent-farm/utils/harness.ts:83-87 explicitly notes that codex/gemini omit session id to avoid this class of crash-loop. Claude has the vulnerability.
Fix sketch
Introduce a "recent failure" detector in setupAutoRestart (packages/codev/src/terminal/session-manager.ts:845-891):
- Track restart timestamps + exit statuses on the session object
- If ≥ 3 restarts fail within 30 seconds AND the failing args include
--resume, strip --resume <uuid> from session.options.args on the next attempt
- Log once:
"resume session <uuid> unrecoverable; retrying without --resume"
- Callback into
state.ts to null out the stored session_id on the architect row so future reconciles don't relearn the poisoned id
Small change; matches the design intent of the harness.ts:83-87 comment (codex/gemini avoid this class; Claude should degrade gracefully into the same state).
Optional defense-in-depth: expose CODEV_SKIP_RESUME=1 env var read by resolveArchitectLaunch (tower-utils.ts:208-236) that strips --resume from launch args regardless of stored state. Emergency escape when the automatic fallback hasn't kicked in yet.
Scope
packages/codev/src/terminal/session-manager.ts — fast-failure detector
packages/codev/src/agent-farm/state.ts — helper to null-out session_id on architect row
- Tests: simulate 3 fast failing spawns with
--resume <bad-uuid>, verify auto-restart drops the --resume flag on attempt 4 and clears the DB row
Related
Problem
After a Tower recovery event (machine crash,
tower startafter downtime, network partition), some architect terminals enter a crash-loop displaying repeated "Could not resume session ..." errors. Users cannot cancel or dismiss the errors — the loop continues until Tower is externally killed or the offending row is manually removed from~/.agent-farm/global.db.Reported by users 2026-07-08.
Root cause (from subagent investigation 2026-07-08)
Both reconcile paths pre-compute
restartOptions.argsfrom the stored session id:packages/codev/src/agent-farm/servers/tower-terminals.ts:661-690(Phase 1 startup reconcile)packages/codev/src/agent-farm/servers/tower-terminals.ts:898-925(on-the-fly reconnect)These
argsare handed toSessionManager.setupAutoRestart(packages/codev/src/terminal/session-manager.ts:845-891), which replayssession.options.argsverbatim afterrestartDelay(2s), up tomaxRestarts(50 —tower-instances.ts:532).If
--resume <uuid>is unresumable (jsonl deleted, corrupted, or foreign), Claude Code printsCould not resume session ...and exits immediately. The exit re-triggers auto-restart with the same args.startRestartResetTimerfires unconditionally after eachspawn()call (not after a "healthy" signal), so a fast-crashing process prevents the counter from ever resetting — count climbs monotonically to 50 (~100 seconds of errors) beforeremoveDeadSessionfinally halts the loop.No escape hatch exists — no env var, no CLI flag, no Tower HTTP endpoint. User must kill Tower or hand-edit
global.dbto break out.Load-bearing context: the comment at
packages/codev/src/agent-farm/utils/harness.ts:83-87explicitly notes that codex/gemini omit session id to avoid this class of crash-loop. Claude has the vulnerability.Fix sketch
Introduce a "recent failure" detector in
setupAutoRestart(packages/codev/src/terminal/session-manager.ts:845-891):--resume, strip--resume <uuid>fromsession.options.argson the next attempt"resume session <uuid> unrecoverable; retrying without --resume"state.tsto null out the storedsession_idon the architect row so future reconciles don't relearn the poisoned idSmall change; matches the design intent of the
harness.ts:83-87comment (codex/gemini avoid this class; Claude should degrade gracefully into the same state).Optional defense-in-depth: expose
CODEV_SKIP_RESUME=1env var read byresolveArchitectLaunch(tower-utils.ts:208-236) that strips--resumefrom launch args regardless of stored state. Emergency escape when the automatic fallback hasn't kicked in yet.Scope
packages/codev/src/terminal/session-manager.ts— fast-failure detectorpackages/codev/src/agent-farm/state.ts— helper to null-outsession_idon architect row--resume <bad-uuid>, verify auto-restart drops the--resumeflag on attempt 4 and clears the DB rowRelated
buildResumehas no ownership check; often triggers this crash-loop on fresh adopt)session_id