Problem
Three HTTP error responses interpolate raw exception details into the body, exposing internals to any client (and to logs / monitoring / browser DevTools that capture error responses):
api/sessions.py:46 — {"error": f"Failed to parse session: {type(e).__name__}: {e}"} on 500. Common shape: "Failed to parse session: KeyError: 'id'" — leaks internal field names. OSError variants leak filesystem paths.
api/sessions.py:69 — same pattern in /stats. Leaks values from ValueError("invalid literal for int() with base 10: '<actual user data>'")-style errors.
api/projects.py:84 — per-session card includes "error_detail": f"{type(e).__name__}: {e}" rendered in the dashboard.
Full traceback.format_exc() is not in HTTP bodies (already logged to stdout only) — but the type(e).__name__ + e interpolation is still a leak.
Suggested fix
Two-line pattern at each site:
except Exception:
current_app.logger.exception("Failed to parse session %s", session_id)
return jsonify({"error": "Failed to parse session"}), 500
logger.exception(...) writes the full traceback + class + message to the server log (operator can debug); the HTTP body returns a stable, generic message. The dashboard already consumes error as a string, so the wire shape doesn't change.
For api/projects.py, drop the error_detail field on the per-session card entirely — the error: True flag is enough to trigger the dashboard's error-state render; the detail string was only used for human inspection and the server log already has it.
A small regression test posts a payload that triggers parse failure and asserts the response body does NOT contain Exception / KeyError / class-name prefixes.
Severity
High / 5pt — listed in Will's eval week-1 plan for claude-code-chat-browser.
Problem
Three HTTP error responses interpolate raw exception details into the body, exposing internals to any client (and to logs / monitoring / browser DevTools that capture error responses):
api/sessions.py:46—{"error": f"Failed to parse session: {type(e).__name__}: {e}"}on 500. Common shape:"Failed to parse session: KeyError: 'id'"— leaks internal field names.OSErrorvariants leak filesystem paths.api/sessions.py:69— same pattern in/stats. Leaks values fromValueError("invalid literal for int() with base 10: '<actual user data>'")-style errors.api/projects.py:84— per-session card includes"error_detail": f"{type(e).__name__}: {e}"rendered in the dashboard.Full
traceback.format_exc()is not in HTTP bodies (already logged to stdout only) — but thetype(e).__name__+einterpolation is still a leak.Suggested fix
Two-line pattern at each site:
logger.exception(...)writes the full traceback + class + message to the server log (operator can debug); the HTTP body returns a stable, generic message. The dashboard already consumeserroras a string, so the wire shape doesn't change.For
api/projects.py, drop theerror_detailfield on the per-session card entirely — theerror: Trueflag is enough to trigger the dashboard's error-state render; the detail string was only used for human inspection and the server log already has it.A small regression test posts a payload that triggers parse failure and asserts the response body does NOT contain
Exception/KeyError/ class-name prefixes.Severity
High / 5pt — listed in Will's eval week-1 plan for
claude-code-chat-browser.