Summary
initSessionState writes session entries to sessions.json using the raw key from resolveSessionKey() (e.g. "settings2"), but every read path (chat.history, sessions.list, sessions.patch) uses resolveSessionStoreKey() which canonicalizes the key (e.g. "agent:main:settings2").
After any WebSocket reconnect (gateway restart, config reload, network blip), the UI calls chat.history with the canonicalized key, finds no entry, and the next chat.send creates a fresh session UUID — silently orphaning the old transcript.
Root Cause
Write path (initSessionState in src/auto-reply/reply/session.ts):
sessionKey = resolveSessionKey(sessionScope, sessionCtxForState, mainKey);
// → returns raw key as-is (e.g. "settings2")
const entry = sessionStore[sessionKey]; // lookup under "settings2"
// ...
store[sessionKey] = sessionEntry; // saves under "settings2"
Read path (loadSessionEntry in src/gateway/session-utils.ts):
const canonicalKey = resolveSessionStoreKey({ cfg, sessionKey });
// → canonicalizes to "agent:main:settings2"
const entry = store[canonicalKey]; // lookup under "agent:main:settings2" — NOT FOUND
The two functions resolve the same input to different keys. The store write uses the short form; every subsequent read uses the canonical form. They never match.
Reproduction
- Open the Control UI webchat
- Type a custom session name (e.g.
settings2) in the session key input
- Send a few messages (session is created and works)
- Trigger a gateway restart (
config.patch, config.apply, SIGUSR1, or just wait for a reconnect)
- The webchat reconnects and calls
chat.history — returns empty (key mismatch)
- Send a new message — a brand new session UUID is generated
- The old transcript file is now orphaned on disk
Fix
Canonicalize the key in initSessionState before any store read/write:
// Before:
sessionKey = resolveSessionKey(sessionScope, sessionCtxForState, mainKey);
// After:
const rawSessionKey = resolveSessionKey(sessionScope, sessionCtxForState, mainKey);
sessionKey = resolveSessionStoreKey({ cfg, sessionKey: rawSessionKey });
This ensures all paths (write during chat.send, read during chat.history, read during sessions.list) use the identical canonical key.
Consolidates These Issues
This is the shared root cause behind multiple open issues:
All of these describe the same symptom: after a restart or reconnect, the gateway cannot find the existing session entry and creates a new one. The orphaned files pile up.
Environment
- OpenClaw: 2026.2.22-2
- OS: macOS (arm64)
- Channel: webchat (Control UI)
- Restart method: in-process SIGUSR1 (config.patch)
Evidence
Traced from source at dist/auto-reply/reply/session.js and dist/gateway/session-utils.js. Confirmed via gateway logs showing session file creation timestamps matching the exact moment of the first post-reconnect chat.send, not the restart itself.
Summary
initSessionStatewrites session entries tosessions.jsonusing the raw key fromresolveSessionKey()(e.g."settings2"), but every read path (chat.history,sessions.list,sessions.patch) usesresolveSessionStoreKey()which canonicalizes the key (e.g."agent:main:settings2").After any WebSocket reconnect (gateway restart, config reload, network blip), the UI calls
chat.historywith the canonicalized key, finds no entry, and the nextchat.sendcreates a fresh session UUID — silently orphaning the old transcript.Root Cause
Write path (
initSessionStateinsrc/auto-reply/reply/session.ts):Read path (
loadSessionEntryinsrc/gateway/session-utils.ts):The two functions resolve the same input to different keys. The store write uses the short form; every subsequent read uses the canonical form. They never match.
Reproduction
settings2) in the session key inputconfig.patch,config.apply, SIGUSR1, or just wait for a reconnect)chat.history— returns empty (key mismatch)Fix
Canonicalize the key in
initSessionStatebefore any store read/write:This ensures all paths (write during
chat.send, read duringchat.history, read duringsessions.list) use the identical canonical key.Consolidates These Issues
This is the shared root cause behind multiple open issues:
sessions.jsonrotation + unlocked store read causes random new sessionIds.jsonlfiles accumulate without cleanupAll of these describe the same symptom: after a restart or reconnect, the gateway cannot find the existing session entry and creates a new one. The orphaned files pile up.
Environment
Evidence
Traced from source at
dist/auto-reply/reply/session.jsanddist/gateway/session-utils.js. Confirmed via gateway logs showing session file creation timestamps matching the exact moment of the first post-reconnectchat.send, not the restart itself.