Skip to content

fix(#260): normalize Rust panic stops from 'breakpoint' to 'exception' - #273

Merged
debugmcpdev merged 2 commits into
mainfrom
fix/260-rust-panic-stop-reason
Aug 6, 2026
Merged

fix(#260): normalize Rust panic stops from 'breakpoint' to 'exception'#273
debugmcpdev merged 2 commits into
mainfrom
fix/260-rust-panic-stop-reason

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

Fixes #260. A Rust panic! pause arrived with lastStop.reason: 'breakpoint' because CodeLLDB implements the rust_panic filter as an internal breakpoint. Callers following the documented crash-diagnosis contract (lastStop.reason === 'exception') missed panic pauses entirely. The normalizeStopReason policy hook added in #270 is exactly the seam this needed.

The discriminator (live-capture driven)

Live capture on Windows/GNU (CodeLLDB 1.11.8) killed the obvious approach: the panic stopped body is only

{"allThreadsStopped":true,"hitBreakpointIds":[2],"reason":"breakpoint","threadId":}

no description/text to pattern-match. But the capture also showed the internal filter breakpoint takes the next free DAP breakpoint id after user breakpoints, so the discriminator is id-based:

Stop body verdict
user breakpoint hit hitBreakpointIds ∩ user ids ≠ ∅ stays 'breakpoint'
step landing on a multi-location bp line (mislabeled, #255 trace) carries the user bp id stays 'breakpoint'
true step completion no hitBreakpointIds untouched
panic (rust_panic internal bp) ids disjoint from user set 'exception' (rawReason: 'breakpoint' preserved)

Both sides must be known: the session manager passes userBreakpointIds only when every user breakpoint has a known adapterId; missing hit ids or incomplete bookkeeping keeps the raw reason. The asymmetry is deliberate — a missed panic keeps a cosmetic 'breakpoint' label, a false 'exception' would mislead agents.

exceptionInfo enrichment guard

After normalization the #243 enrichment would have fired (CodeLLDB advertises supportsExceptionInfoRequest), and live it answers about the internal breakpoint{exceptionId: "Breakpoint", description: "breakpoint 1.1"} — misleading, not enriching. Enrichment is now additionally gated on the adapter's raw reason being 'exception'. The panic message itself is available via get_output stderr (thread 'main' panicked at …), which the e2e asserts.

Also

Testing

Note for review: the Linux leg of the capture is delegated to ubuntu CI — if Linux CodeLLDB reported panics natively as 'exception' the hook no-ops and the e2e still passes; if it reported 'breakpoint' without ids the e2e would fail visibly here.

🤖 Generated with Claude Code

CodeLLDB implements the rust_panic filter as an internal breakpoint, so
a panic pause arrived as reason 'breakpoint' and callers following the
documented contract (lastStop.reason === 'exception') missed it.

Live capture (Windows/GNU, CodeLLDB 1.11.8) showed the stopped body is
only {allThreadsStopped, hitBreakpointIds, reason, threadId} — there is
no description/text to match on — so the discriminator is breakpoint-id
based: the internal filter breakpoint takes the next free id after user
breakpoints, and its hitBreakpointIds are disjoint from every user
breakpoint id.

- normalizeStopReason context gains userBreakpointIds, passed by the
  session manager only when every user breakpoint has a known adapterId
  (incomplete bookkeeping disables the inference).
- RustAdapterPolicy maps a 'breakpoint' stop to 'exception' when hit ids
  are present and disjoint from the user set. No hit ids (the mislabeled
  step completions from the issue #255 trace) or no user-id knowledge
  keeps the raw reason — a missed panic is cosmetic, a false 'exception'
  would mislead.
- exceptionInfo enrichment now also requires the adapter's RAW reason to
  be 'exception': for a normalized panic CodeLLDB answers about the
  internal breakpoint ({exceptionId: 'Breakpoint', description:
  'breakpoint 1.1'}, confirmed live) — misleading, not enriching. The
  panic message is available via get_output stderr.
- Policy comment documents that CodeLLDB 1.11.8 no longer advertises
  rust_panic (only cpp_throw/cpp_catch) yet the filter still works; the
  capability drift warning remains the early alarm.
- New examples/rust/panic_example + Rust launch trio in the
  break-on-exceptions e2e (default pause at panic + exit 101 after
  continue, explicit 'uncaught', 'none' opt-out).

Fixes #260

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit ef23662 into main Aug 6, 2026
9 checks passed
@debugmcpdev
debugmcpdev deleted the fix/260-rust-panic-stop-reason branch August 6, 2026 03:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rust: panic pause reports lastStop.reason 'breakpoint' instead of 'exception'; CodeLLDB no longer advertises rust_panic

2 participants