fix(tui): log 4001 session-not-found rejections for diagnosability
Messages sent into a session whose in-memory runtime was detached on WS disconnect and orphan-reaped vanished silently: _sess_nowait returned 4001 with no log line, so 'request arrived and was rejected' was indistinguishable from 'request never arrived' in a 'message vanished' report. Log a WARNING with the session id and request id on every session-scoped RPC rejected against an unknown runtime id. Adds a regression test asserting the 4001 response and the warning. Closes #90428
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -341,6 +342,38 @@ def test_compute_host_explicit_images_do_not_clear_later_attachment(monkeypatch)
|
||||
assert session["attached_images"] == ["/tmp/c.png"]
|
||||
|
||||
|
||||
def test_prompt_submit_unknown_session_logs_warning(caplog):
|
||||
"""A submit against a reaped runtime id must leave a diagnosable trace.
|
||||
|
||||
Regression for #90428: messages sent into a session whose in-memory
|
||||
runtime was detached on WS disconnect and orphan-reaped vanished
|
||||
silently — the 4001 was never logged, so "request arrived and was
|
||||
rejected" was indistinguishable from "request never arrived".
|
||||
"""
|
||||
for session in list(server._sessions.values()):
|
||||
server._teardown_session(session)
|
||||
server._sessions.clear()
|
||||
|
||||
with caplog.at_level(logging.WARNING, logger="tui_gateway.server"):
|
||||
resp = _dispatch_sync(
|
||||
{
|
||||
"id": "r1",
|
||||
"method": "prompt.submit",
|
||||
"params": {"session_id": "gone-sid", "text": "hello"},
|
||||
}
|
||||
)
|
||||
|
||||
assert resp == {
|
||||
"jsonrpc": "2.0",
|
||||
"id": "r1",
|
||||
"error": {"code": 4001, "message": "session not found"},
|
||||
}
|
||||
assert any(
|
||||
"session-scoped RPC rejected" in rec.message and "gone-sid" in rec.message
|
||||
for rec in caplog.records
|
||||
)
|
||||
|
||||
|
||||
def test_prompt_submit_fails_open_inline_when_compute_host_dispatch_breaks(monkeypatch):
|
||||
class _BrokenSupervisor:
|
||||
def submit_turn(self, frame, *, on_complete=None):
|
||||
|
||||
+19
-2
@@ -2741,8 +2741,25 @@ def _start_agent_build(sid: str, session: dict) -> None:
|
||||
|
||||
|
||||
def _sess_nowait(params, rid):
|
||||
s = _sessions.get(params.get("session_id") or "")
|
||||
return (s, None) if s else (None, _err(rid, 4001, "session not found"))
|
||||
sid = params.get("session_id") or ""
|
||||
s = _sessions.get(sid)
|
||||
if s:
|
||||
return (s, None)
|
||||
# A session-scoped RPC hit a runtime id the gateway no longer holds
|
||||
# (detached on WS disconnect and orphan-reaped, LRU-evicted, or torn down
|
||||
# after an idle TTL). The client is expected to recover via
|
||||
# session.resume on the STORED session id, but a plain stale-id send
|
||||
# leaves no trace anywhere when the resume never fires — every RPC in
|
||||
# this class returned a silent 4001. Log it so a "message vanished"
|
||||
# report is diagnosable as "request arrived and was rejected" instead of
|
||||
# "request never arrived" (see #90428).
|
||||
logger.warning(
|
||||
"session-scoped RPC rejected: session_id=%r not in memory "
|
||||
"(detached/reaped runtime; client should resume the stored session), rid=%r",
|
||||
sid,
|
||||
rid,
|
||||
)
|
||||
return (None, _err(rid, 4001, "session not found"))
|
||||
|
||||
|
||||
def _sess(params, rid):
|
||||
|
||||
Reference in New Issue
Block a user