New request
#2782: GET /api/session returns 200 for missing sessions but POST 404s — UI stuck with no self-heal path
We'll provision a sandbox, run an agent against the issue, and open a draft PR. You can pull the branch and iterate from there.
Summary
GET /api/session?session_id=<missing> returns HTTP 200 (with a tolerant payload) while POST /api/session/draft and POST /api/chat/start return 404 "Session not found" for the same missing session. This inconsistency prevents the studio UI from self-healing after a session is deleted server-side — the browser polls GET, sees 200, and never triggers the localStorage-clear / redirect-to-root branch. The user is stuck on a dead session with no in-app recovery path.
Compounding it: _sessionIdFromLocation() in static/sessions.js reads the session id from the URL path (/session/<id>) and uses it ahead of localStorage['hermes-webui-session'] in static/boot.js:1605-1607. So clearing localStorage doesn't help either — the URL keeps re-injecting the dead id on every reload.
Repro
- Open studio, create a session, note the URL path (
/session/abc123). - Server-side, delete the session file (or rebuild the container so the in-container state dir resets):
docker compose up -d --force-recreate hermes-webui - Reload the studio tab on the same URL.
- Try to send a chat.
Expected: UI realises the session is gone and redirects to a working state (empty + button, or restore from _index.json).
Actual: UI shows "Error: Session not found" on every send. Network tab: GET /api/session?session_id=abc123 → 200; POST /api/session/draft and /api/chat/start → 404.
Server-side code
api/routes.pyGET/api/sessionhandler (around line 3695) — wrapsget_session(sid)in a try/except but returns 200 with tolerant content rather than 404.api/routes.pyPOST/api/session/draft(around line 4920) and/api/chat/start(around line 5312) —get_session()KeyError → 404 "Session not found".
Client-side code
static/boot.js:1605-1607—const saved = urlSession || savedLocal;URL wins over localStorage unconditionally.static/sessions.js:568— there's a self-heal branch that doeslocalStorage.removeItem('hermes-webui-session')on a 404, but it's reached only under narrow conditions and not when the URL forces the bad id.
Suggested fix
Either of the following would close the loop. Both would be ideal:
- Server: make GET
/api/sessionreturn 404 (consistent with POST) when the session_id is unknown. This lets the existing client-side self-heal branch trigger on the next poll. - Client: when ANY session-scoped endpoint returns 404, strip
/session/<id>from the URL (history.replaceState), clearhermes-webui-sessionfrom localStorage, and reset to the empty state. This makes the UI resilient to server-side session resets regardless of where the bad id came from.
Environment
- hermes-webui pulled from
ghcr.io/nesquena/hermes-webui@sha256:d568b23f97243f11a155d26a5757584751306d72be0c04ecfbac07417a795794(currently running on my docker-host). - Triggered after I rebuilt the container during a multi-phase Hermes infra migration on 2026-05-22. Tab was still open on a
/session/<old-id>URL from before the rebuild. - Worked around by manually navigating to the bare root URL
https://studio.faneite.me/and starting a new chat from there.
Happy to test a fix on my setup if useful.
Inconsistent HTTP semantics between GET and POST session endpoints require coordinated fixes in API handlers and frontend session-resolution / boot logic.
- api/routes.py
- api/session.py
- static/boot.js
- static/sessions.js
- static/api.js