Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
When a WebChat client reconnects after a sleep cycle, gateway restart, or network disconnect that spans the daily session-reset boundary (default: 04:00 local), the first chat.send after reconnect silently starts a new session. The old session transcript is renamed to <id>.jsonl.reset.<ISO-timestamp>, the chat history is wiped from the UI, and no error or warning is shown. Users report this as "messages disappeared" or "the conversation was lost."
This is a code-level analysis of the root cause, filed to complement the symptom reports in #87700 and #70330 (gateway-restart variant), neither of which identifies the specific check that causes the silent reset.
Steps to reproduce
- Open the Control UI WebChat. Exchange several messages. Note the active session ID.
- Leave the browser open and let the host machine sleep through 04:00 (the default
session.reset.atHour).
- Wake the machine. The WebChat still shows the prior conversation (UI has not refreshed).
- Type any new message and send it.
Result: The chat clears instantly, no error is shown, the old .jsonl transcript is renamed to <id>.jsonl.reset.*, and the conversation is effectively lost from the UI. The Docker/gateway log shows no explicit /reset command — the reset is a side-effect of the freshness check.
Expected behavior
Either:
- (A) The session is resumed silently without rename (if the 4 AM boundary has not crossed OR a grace period is respected), OR
- (B) The UI receives a proactive stale-session event so it can display a clear "Starting a new session — previous session archived" notice before the user types a message.
The current behavior (silently discard history on first send) gives no opportunity for the user to save or copy the prior conversation.
Actual behavior
The session is silently replaced. <id>.jsonl is archived to <id>.jsonl.reset.<ISO>. The Control UI clears and shows an empty conversation. The user only discovers the loss after sending their next message.
Code analysis (root cause — analysis, not observed in live environment)
The gateway evaluates session freshness in src/gateway/server-methods/agent.ts (line 1657–1660) before deciding whether to reuse an existing session:
// src/gateway/server-methods/agent.ts:1657-1660
const canReuseSession =
Boolean(entry?.sessionId) &&
(freshness?.fresh ?? false) && // ← fails when staleDaily=true
!failedSessionTranscriptMissing;
freshness is produced by evaluateSessionFreshness in src/config/sessions/reset-policy.ts:
// src/config/sessions/reset-policy.ts:84-93
const dailyResetAt =
params.policy.mode === "daily"
? resolveDailyResetAtMs(params.now, params.policy.atHour) // default: 4 AM today
: undefined;
const staleDaily = dailyResetAt != null && sessionStartedAt < dailyResetAt;
// ...
return { fresh: !(staleDaily || staleIdle), ... };
resolveDailyResetAtMs returns the most recent 04:00 wall-clock timestamp. Any session whose sessionStartedAt predates that timestamp is considered stale (fresh = false).
Default values (src/config/sessions/reset-policy.ts:21-22):
export const DEFAULT_RESET_MODE: SessionResetMode = "daily";
export const DEFAULT_RESET_AT_HOUR = 4;
When canReuseSession = false and no requestedSessionId is provided, a new UUID is generated and isNewSession = true (agent.ts:1668-1671). This triggers archiveSessionTranscriptsForSessionDetailed with reason: "reset", which renames the transcript (session-reset-service.ts:964-969).
The WebChat client has no signal that the session is stale until after the first chat.send completes. At that point the UI observes a session rotation event and clears the chat display — indistinguishable from a silent data loss.
Hypothesis
Fix options (A/B/C — not implemented, proposed for discussion):
Option A — Grace period on reconnect: After a WebSocket reconnect event, delay freshness evaluation by N minutes (e.g. 5 min). This prevents the 4 AM boundary from triggering mid-session for users who simply had a brief disconnect. Lowest UI impact but reduces daily-reset reliability.
Option B — Proactive stale notification: When a webchat client re-connects (identified via isWebchatConnect), check if its current session is stale and emit a session.stale event to the client. The UI can then display "Your session was reset at 4:00 AM — previous conversation is archived" before the user sends a message. Requires a new event type on the client.
Option C — Explicit resume intent in chat.send: Require webchat clients to include the known sessionId in every chat.send. If the gateway detects a stale session but the client is resuming the same sessionId, respond with a structured session.reset_pending error carrying the new sessionId, giving the UI a chance to surface a notice and refresh. Requires a protocol change.
Option B has the best user-experience profile and is consistent with how other notifications (session compaction, memory, heartbeat) are pushed to the Control UI.
Related issues
OpenClaw version
Analyzed against main @ 01dda73e9bb1ecb787e2762612fc3bb586d731a0 (confirmed by #87700 reporter). Behavior reproduced on 2026.5.5+.
Operating system
Confirmed on macOS + Docker (arm64) per #87700; same code path applies to all platforms.
Install method
Not applicable — this is a code-path analysis.
Additional information
Relevant files (for reviewer orientation):
src/config/sessions/reset-policy.ts — evaluateSessionFreshness, resolveDailyResetAtMs, DEFAULT_RESET_MODE, DEFAULT_RESET_AT_HOUR
src/gateway/server-methods/agent.ts — canReuseSession check (line 1657–1672), isNewSession assignment (line 1668–1672)
src/gateway/session-reset-service.ts — archiveSessionTranscriptsForSessionDetailed (line 964) — produces the .jsonl.reset.* files
AI-assisted disclosure: This issue was filed by an automated contribution scout. The code analysis is static (grep + file reads on the source tree), not live observation. The hypothesis sections are marked as such. A human should verify the suggested fix shape before implementation.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
When a WebChat client reconnects after a sleep cycle, gateway restart, or network disconnect that spans the daily session-reset boundary (default: 04:00 local), the first
chat.sendafter reconnect silently starts a new session. The old session transcript is renamed to<id>.jsonl.reset.<ISO-timestamp>, the chat history is wiped from the UI, and no error or warning is shown. Users report this as "messages disappeared" or "the conversation was lost."This is a code-level analysis of the root cause, filed to complement the symptom reports in #87700 and #70330 (gateway-restart variant), neither of which identifies the specific check that causes the silent reset.
Steps to reproduce
session.reset.atHour).Result: The chat clears instantly, no error is shown, the old
.jsonltranscript is renamed to<id>.jsonl.reset.*, and the conversation is effectively lost from the UI. The Docker/gateway log shows no explicit/resetcommand — the reset is a side-effect of the freshness check.Expected behavior
Either:
The current behavior (silently discard history on first send) gives no opportunity for the user to save or copy the prior conversation.
Actual behavior
The session is silently replaced.
<id>.jsonlis archived to<id>.jsonl.reset.<ISO>. The Control UI clears and shows an empty conversation. The user only discovers the loss after sending their next message.Code analysis (root cause — analysis, not observed in live environment)
The gateway evaluates session freshness in
src/gateway/server-methods/agent.ts(line 1657–1660) before deciding whether to reuse an existing session:freshnessis produced byevaluateSessionFreshnessinsrc/config/sessions/reset-policy.ts:resolveDailyResetAtMsreturns the most recent 04:00 wall-clock timestamp. Any session whosesessionStartedAtpredates that timestamp is considered stale (fresh = false).Default values (
src/config/sessions/reset-policy.ts:21-22):When
canReuseSession = falseand norequestedSessionIdis provided, a new UUID is generated andisNewSession = true(agent.ts:1668-1671). This triggersarchiveSessionTranscriptsForSessionDetailedwithreason: "reset", which renames the transcript (session-reset-service.ts:964-969).The WebChat client has no signal that the session is stale until after the first
chat.sendcompletes. At that point the UI observes a session rotation event and clears the chat display — indistinguishable from a silent data loss.Hypothesis
Fix options (A/B/C — not implemented, proposed for discussion):
Option A — Grace period on reconnect: After a WebSocket reconnect event, delay freshness evaluation by N minutes (e.g. 5 min). This prevents the 4 AM boundary from triggering mid-session for users who simply had a brief disconnect. Lowest UI impact but reduces daily-reset reliability.
Option B — Proactive stale notification: When a webchat client re-connects (identified via
isWebchatConnect), check if its current session is stale and emit asession.staleevent to the client. The UI can then display "Your session was reset at 4:00 AM — previous conversation is archived" before the user sends a message. Requires a new event type on the client.Option C — Explicit resume intent in
chat.send: Require webchat clients to include the knownsessionIdin everychat.send. If the gateway detects a stale session but the client is resuming the samesessionId, respond with a structuredsession.reset_pendingerror carrying the newsessionId, giving the UI a chance to surface a notice and refresh. Requires a protocol change.Option B has the best user-experience profile and is consistent with how other notifications (session compaction, memory, heartbeat) are pushed to the Control UI.
Related issues
OpenClaw version
Analyzed against
main@01dda73e9bb1ecb787e2762612fc3bb586d731a0(confirmed by #87700 reporter). Behavior reproduced on 2026.5.5+.Operating system
Confirmed on macOS + Docker (arm64) per #87700; same code path applies to all platforms.
Install method
Not applicable — this is a code-path analysis.
Additional information
Relevant files (for reviewer orientation):
src/config/sessions/reset-policy.ts—evaluateSessionFreshness,resolveDailyResetAtMs,DEFAULT_RESET_MODE,DEFAULT_RESET_AT_HOURsrc/gateway/server-methods/agent.ts—canReuseSessioncheck (line 1657–1672),isNewSessionassignment (line 1668–1672)src/gateway/session-reset-service.ts—archiveSessionTranscriptsForSessionDetailed(line 964) — produces the.jsonl.reset.*filesAI-assisted disclosure: This issue was filed by an automated contribution scout. The code analysis is static (grep + file reads on the source tree), not live observation. The hypothesis sections are marked as such. A human should verify the suggested fix shape before implementation.