fix(#3993): add stale-load guard before self-heal so a superseded boot load can't wipe a healthy session (Codex CORE race)

currentSid is snapshotted before the awaited /api/session fetch; if the user
clicks a healthy session while a boot-time restore is in flight and that boot
load then fails non-404, _clearStuckSessionOnBoot(sid, null) would wipe the
healthy session's localStorage/URL. Guard the catch block on
_loadingSessionId !== sid (a newer load superseded this one) — re-arm the active
stream and bail before any self-heal/DOM mutation. Protects both the non-404 and
404 inline self-heal paths. + regression test.
This commit is contained in:
nesquena-hermes
2026-06-11 22:27:01 +00:00
parent 7e341a0d70
commit 8e7d3b086d
2 changed files with 35 additions and 0 deletions

View File

@@ -921,6 +921,17 @@ async function loadSession(sid){
data = await api(`/api/session?session_id=${encodeURIComponent(sid)}&messages=0&resolve_model=0`);
} catch(e) {
const _msgInner = $('msgInner');
// Stale-load guard (Codex): a newer loadSession() may have started while this
// request was awaiting (e.g. the user clicked a healthy session during a
// boot-time restore). currentSid was snapshotted before the await, so without
// this guard a failed superseded load could self-heal (wipe localStorage/URL)
// for the session the user actually navigated to. If we no longer own the
// load, re-arm the active session's stream and bail before any DOM mutation
// or self-heal.
if (_loadingSessionId !== sid) {
_rearmActiveSessionStream();
return;
}
if(_msgInner){
if(e.status===404){
_msgInner.innerHTML='<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted);font-size:14px;padding:40px;text-align:center;">Session not available in web UI.</div>';

View File

@@ -75,3 +75,27 @@ def test_does_not_clear_when_viewing_a_healthy_session():
data = _run_helper("'live-session-123'")
assert data["removed"] is False
assert data["replaced"] is False
def test_stale_load_guard_present_before_self_heal():
"""A superseded in-flight load (a newer loadSession started during the await)
must bail BEFORE any self-heal/DOM mutation, so a failed boot restore can't
wipe localStorage/URL for the session the user navigated to mid-flight (Codex
race finding). The guard re-arms the active stream and returns."""
js = _read(SESSIONS_JS)
# Anchor on the self-heal CALL (unique; the bare name also appears in the
# helper's docstring), then look at the preceding window of the same
# loadSession catch block for the stale-load guard.
heal_idx = js.index("_clearStuckSessionOnBoot(sid, currentSid);")
block = js[heal_idx - 2400: heal_idx + 60]
guard = "if (_loadingSessionId !== sid) {"
assert guard in block, "stale-load guard missing from the loadSession catch block"
# The guard must come BEFORE the self-heal call (so a superseded load can't clear).
assert block.index(guard) < block.index("_clearStuckSessionOnBoot(sid, currentSid);"), \
"stale-load guard must precede _clearStuckSessionOnBoot"
# And before the 404 inline clear too.
assert block.index(guard) < block.index("localStorage.removeItem('hermes-webui-session')"), \
"stale-load guard must precede the 404 inline self-heal"
# It re-arms the active stream rather than leaving it torn down.
guard_tail = block[block.index(guard): block.index(guard) + 120]
assert "_rearmActiveSessionStream()" in guard_tail