fix(gateway): log start session persistence failures#100313
fix(gateway): log start session persistence failures#100313masatohoshino wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 5, 2026, 9:21 AM ET / 13:21 UTC. Summary PR surface: Source +7, Tests +26. Total +33 across 2 files. Reproducibility: yes. at source level. Current main catches rejected start lifecycle persistence writes with Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the narrow gateway log and regression test after the QA Smoke CI failure is rerun or triaged, keeping session persistence semantics unchanged. Do we have a high-confidence way to reproduce the issue? Yes, at source level. Current main catches rejected start lifecycle persistence writes with Is this the best way to solve the issue? Yes. Logging once at the start-persistence catch in AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against f9e194e4a122. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +7, Tests +26. Total +33 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (1 earlier review cycle)
|
The gateway session-lifecycle "start" event persistence (persistGatewaySessionLifecycleEvent, which surfaces write failures via requireWriteSuccess) was fired as void ...catch(() => undefined), swallowing the rejection with zero logging. A failed start-marker write silently dropped the run's start record from restart-recovery accounting, with no operator-visible trace. The sibling terminal-phase catch already logs this since openclaw#97839; the start path was the unfixed sibling. Mirror that fix: log the swallowed start-phase persistence failure with the same redacted message shape via formatForLog, keeping the fire-and-forget semantics unchanged. Adds a focused regression test asserting the log fires on start-persist rejection.
64cca25 to
e8f4c93
Compare
What Problem This Solves
Resolves an issue where operators lose all visibility when a gateway session's
start lifecycle event fails to persist. When the session-lifecycle store
rejects the write for a
"start"event, the run's start marker is silentlydropped from restart-recovery accounting with no log line, so an operator
investigating a session that never resumed after a restart has no trace of the
failed write.
The sibling terminal-phase persistence failure was made observable in
#97839 (
log terminal session persistence failures), but the start-phase pathwas left swallowing its rejection.
Why This Change Was Made
The start-phase persist was fired as
void persistGatewaySessionLifecycleEvent({...}).catch(() => undefined)— therejection was discarded with zero logging, while the terminal-phase sibling in
the same file already logs the equivalent failure. This change mirrors #97839:
the start-phase catch now emits a redacted
logErrorwith the same messageshape (via
formatForLog), so both lifecycle phases surface persistencefailures consistently. The fire-and-forget semantics are unchanged — this is
limited to adding the missing log line, with no change to recovery behavior.
User Impact
Operators and maintainers now get a logged, redacted diagnostic
(
gateway: start session persistence failed session=… run=… error=…) when asession's start marker fails to persist, matching the existing terminal-phase
log. This closes an asymmetric blind spot in restart-recovery diagnostics. No
user-visible runtime behavior changes.
Evidence
Real behavior proof (genuine read-only-filesystem rejection, no mocks)
Drove the shipped
createAgentEventHandlerend-to-end against a realsession-store persistence failure — the production analog of a read-only disk
or filesystem holding session state. A real on-disk session entry is seeded
via the prod
upsertSessionEntryAPI while the fs is writable, then itsdirectory is remounted read-only (EROFS) inside a user+mount namespace.
Reads still succeed (so
loadSessionEntryfinds the entry); the real atomicwrite (
@openclaw/fs-safereplaceFileAtomic→fs.writeFiletemp) then failswith
EROFS,requireWriteSuccess: truepropagates the rejection, and thehandler's start-phase
.catchfires. Novi.mock; the persistence function andlogError/formatForLogare the real production modules.Before this change the same run produces no log line (the rejection is
swallowed by
.catch(() => undefined)); after it, the redacted diagnostic aboveis emitted, mirroring the terminal-phase behavior shipped in #97839.
Focused regression test
A colocated regression test (
server-chat.agent-events.test.ts), modeled on theexisting terminal-failure test and the existing start-persist success test,
forces the start persist to reject and asserts the redacted log fires exactly
once:
Static checks
oxfmt --check— clean;oxlint— cleantsgo:core— exit 0The change is confined to
src/gateway/server-chat.ts(the start-phase catch)plus its colocated test. No config, migration, or protocol surface touched.