Bug
runner.ts crashes in a tight loop when session.json exists but is missing the sessionId field. The daemon restarts via watchdog, crashes again on the next heartbeat, and repeats indefinitely.
Root cause
getSession() in sessions.ts reads session.json and returns the parsed object. If the file contains {"turnCount":0,"compactWarned":false,"lastUsedAt":"..."} (no sessionId or createdAt), getSession() returns a truthy object with sessionId: undefined.
In runner.ts (~line 1010):
existing is truthy (it's an object), so isNew is false. Then ~line 1047:
console.log(
`... (${isNew ? "new session" : `resume ${existing!.sessionId.slice(0, 8)}`} ...)`
);
existing!.sessionId is undefined, so .slice() throws:
TypeError: undefined is not an object (evaluating 'existing.sessionId.slice')
How the corruption happens
I'm not 100% sure what originally wrote the incomplete session.json, but it may be a race between a session write and a crash/kill, or an edge case in the session-creation path where the file is written before sessionId is assigned.
Fix
- const isNew = !existing;
+ const isNew = !existing || !existing.sessionId;
This treats a corrupted session (missing sessionId) as a new session, triggering a fresh bootstrap instead of crashing.
A secondary hardening could also be applied at line ~1047:
- `resume ${existing!.sessionId.slice(0, 8)}`
+ `resume ${existing!.sessionId?.slice(0, 8) ?? "unknown"}`
Environment
- ClaudeClaw v1.0.27
- Bun runtime on Windows 10
- Crash confirmed in daemon log with 3+ restart cycles before manual intervention
Workaround
Delete the corrupted session.json and restart the daemon. A fresh session bootstraps normally.
Bug
runner.tscrashes in a tight loop whensession.jsonexists but is missing thesessionIdfield. The daemon restarts via watchdog, crashes again on the next heartbeat, and repeats indefinitely.Root cause
getSession()insessions.tsreadssession.jsonand returns the parsed object. If the file contains{"turnCount":0,"compactWarned":false,"lastUsedAt":"..."}(nosessionIdorcreatedAt),getSession()returns a truthy object withsessionId: undefined.In
runner.ts(~line 1010):existingis truthy (it's an object), soisNewisfalse. Then ~line 1047:existing!.sessionIdisundefined, so.slice()throws:How the corruption happens
I'm not 100% sure what originally wrote the incomplete
session.json, but it may be a race between a session write and a crash/kill, or an edge case in the session-creation path where the file is written beforesessionIdis assigned.Fix
This treats a corrupted session (missing
sessionId) as a new session, triggering a fresh bootstrap instead of crashing.A secondary hardening could also be applied at line ~1047:
Environment
Workaround
Delete the corrupted
session.jsonand restart the daemon. A fresh session bootstraps normally.