fix(sidecar): keep the exception type and innermost frame per traceback - #1950
Conversation
The sidecar stream sanitizer collapsed every Python traceback into a single "sidecar emitted an unexpected exception" line, printed once per stream. On .github#1812's strix run 33993155419 the gateway answered 83 requests with 500 internal_error -- contextual-orchestrator's generic `except Exception: traceback.print_exc(); _send_error(500, ...)` path -- and no artifact could say which exception escaped, where, or how many times. Each traceback now yields one bounded line, `unexpected_exception type=<Type> frame=contextual_orchestrator/<module>.py:<line>:<fn>`: opened at the header, closed at the column-0 terminal line (or the next header, an allowlisted line, or end of stream -> type=unknown). Indented frame/source lines are consumed rather than counted as omitted; only a contextual_orchestrator frame's package path, line and function are kept, and the exception message is never re-emitted. Chain sentences are consumed so a chained exception yields cause then effect. Tests render real tracebacks with traceback.format_exc() from code compiled under a contextual_orchestrator/ filename (plain, dotted package type with a `from` chain, truncated at end of stream, free-text column-0 line), and the existing traceback expectation is updated. Only the sanitizer's own test consumed the old sentence. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Gate on Negative control: with Design notes: blank lines are skipped outright (Python pads chain sentences with them; they carry nothing); a column-0 line inside a traceback that is neither a terminal |
|
Warning Review limit reachedNext included review available in 27 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (3)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Sidecar pin |
|
Re-merged |
|
Re-merged |
|
Independent head-tree verification and merge under the chicken-and-egg clause (host 1, 2026-09-06 14:2x KST; merger ≠ author). Requested by lane peer 1 after host 2 did not respond to two merge requests; verified from a fresh checkout of the pushed head, not from the author's report.
|
Brings the branch current with protected main (24 commits, including the contextual-orchestrator pin advance in efb8926 and the sidecar preflight changes in #1947/#1949/#1950) so the gap-baseline entry this PR carries can be extended with the retry-stacking root cause and its first post-pin production measurement. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX
Brings the branch current with protected main (24 commits since 6f8c51d). main did not touch .github/workflows/opencode-review-dispatch.yml in that range, so this branch's REVIEW_DISPATCH_BLOB_SHA pin (0a39def) still matches the merged workflow; the only textual merge was CHANGELOG.md, which auto-merged. The merged main carries the contextual-orchestrator pin advance (efb8926) and the sidecar preflight repairs (#1947, #1949, #1950), so this head's required reviews run against the repaired gateway instead of the retry-stacking pin that failed the previous head's noema-review (502 after 2343 s) and strix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KPmJErfkcHer4UVEgrQxUX
Problem
.github#1812's strix run33993155419(new sidecar, pin464da471) ended after 9014 s on the gateway'sHTTP 500 {'error': {'code': 'internal_error'}}— contextual-orchestrator's genericexcept Exception: traceback.print_exc(); _send_error(500, "internal_error", …)path, which prints one traceback per unhandled exception. The sidecar stderr artifact holds 83request_failed status=500 code=internal_errorlines and exactly onesidecar emitted an unexpected exceptionline:scripts/ci/sanitize_contextual_orchestrator_sidecar_stream.pydrops every traceback line and prints that sentence once per stream, so neither the exception type, nor where it escaped, nor how many times, survives intostrix-reports/noema-sidecar-evidence. The observability lane (#1943/#1944/#1945) made provider walks readable; unhandled exceptions stayed opaque. Diagnosis on #1812: #1812 (comment)Related: the escaping exception itself is fixed at its canonical owner in contextual-orchestrator#1082 (a bare
TimeoutErroron the tools/passthrough path is recorded on the breaker and classified to502 provider_connection_error, still without replay). Acceptance chain: after this PR, the first strix failure artifact should carryunexpected_exception type=TimeoutError frame=contextual_orchestrator/orchestrator.py:…:_send_raw; once the sidecar pin passes #1082, that line disappears in favour of502 provider_connection_errorplus acircuit_failureevent. Note the sidecar pin moved to414f2297(contextual-orchestrator#1081) in.github@efb89269— the orchestrator templates this sanitizer admits were re-checked against that revision (see the PR thread).Change
unexpected_exception type=<ExceptionType> frame=contextual_orchestrator/<module>.py:<line>:<function>. A traceback opens at itsTracebackheader and closes at the column-0 terminalType: messageline, at the next header or allowlisted line, or at end of stream (type=unknown). Indented frame/source lines are consumed (not counted as omitted); only acontextual_orchestratorframe's package-relative path, line and function are kept (innermost wins). The two chain sentences are consumed, so a chained exception yields cause then effect.[A-Za-z_]\w{0,63}dotted up to 9 segments), the function by[A-Za-z0-9_<>]{1,80}.Tests
test_sidecar_stream_sanitizer_keeps_exception_type_and_innermost_frame: a real traceback rendered bytraceback.format_exc()from code compiled under a…/contextual_orchestrator/server.pyfilename (do_POST → _serve → KeyError) →type=KeyError frame=contextual_orchestrator/server.py:2:_serve; the secret in the payload and the test file's own frame are absent.…_keeps_dotted_exception_types_and_chains: package-definedProviderResponseErrorraisedfromaKeyError→ two lines, cause then effect, dotted type kept, message (sk-leak) absent.…_closes_a_truncated_traceback_at_end_of_stream: sidecar dies mid-traceback →type=unknown frame=contextual_orchestrator/orchestrator.py:7824:_invoke.…_does_not_treat_free_text_as_an_exception: a column-0 free-text line closes the traceback and is counted as omitted.…_summarizes_unstructured_and_traceback_linesexpectation updated (two headers → twounknownlines; the indented pseudo-frame no longer counts as omitted).Evidence
Gate on the head with
GITHUB_ACTIONS=true: see the PR's first comment (full suite / coverage / interrogate figures). Negative control: the four new tests fail onmain's sanitizer.Developer experience: a Noema/Strix failure whose sidecar stderr shows
unexpected_exception type=… frame=…names the escaping exception and the orchestrator frame directly in the artifact;omitted_unstructured_linesno longer absorbs traceback bodies.User experience: none (CI evidence only).
Acceptance: the next
noema-sidecar-evidence/strix-reportsproduced after this lands, for a run that hits gateway500 internal_error, carries oneunexpected_exception type=<T> frame=contextual_orchestrator/…line per 500 instead of a singlesidecar emitted an unexpected exception.🤖 Generated with Claude Code