Skip to content

Name the call site in non-Gleam crash reports - #10

Merged
richardcocks merged 8 commits into
mainfrom
crash-site-reporting
Aug 21, 2026
Merged

richardcocks merged 8 commits into
mainfrom
crash-site-reporting

Conversation

@richardcocks

Copy link
Copy Markdown
Owner

Problem

A test that dies with a non-Gleam error reports only the bare reason. The motivating real-world case: a stale filepath.beam shadowing the filepath package turned every test in one module into an information-free Crashed: Undef. The M:F/A identifying the failure was sitting in the stacktrace''s first frame - {filepath, split, [<<"gleam.toml">>], []} - but catch_panic discarded __STACKTRACE__, so nothing downstream could report it.

Change

  • catch_panic captures the stacktrace and returns {error, {Reason, Stacktrace}} - deliberately the same shape as BEAM exit reasons, so a single split_crash/1 serves both the caught-panic and process-death classification paths. find_panic''s recursive tuple search keeps Gleam-panic decoding working through the extra tuple (guarded by the existing OTP-wrapping spec).
  • split_crash/1 reduces the trace to a CrashSite (module, function, arity, file/line when the frame carries them); UnknownDetail and ExitDetail carry it as Option(CrashSite). The inspectable reason stays stacktrace-free, so no reporter ever dumps raw frames.
  • Console output becomes Crashed: Undef calling config_parser:parse/1, plus an at file:line second line when the frame has a location (undef frames never do - the function does not exist). TeamCity and JUnit embed the same summary via a shared describe.crash; JSONL gains structured site_module / site_function / site_arity (plus site_file / site_line) fields mirroring the todo outcome''s site_* naming.
  • JavaScript is unchanged by design: a JS stacktrace is an unparsed string, so there is never a site, and the fallback wording is pinned by spec.

Coverage

  • New "Crash sites" spec section: a live undef via a fixture external to a deliberately nonexistent module, a linked-process death with the same shape, and synthetic-site rendering specs for describe and all three reporters.
  • Playground gains crashing_config_test - undef on the BEAM, a TypeError-throwing FFI on JavaScript (the module must exist there: a missing .mjs import would break discovery of the whole suite at import time).
  • The e2e suite asserts the site fields through the subprocess JSONL stream on both targets; playground tallies move from 3 to 4 failures.

Both targets green: 80 passed (Erlang, including the four e2e subprocess runs), 67 passed (JavaScript).

🤖 Generated with Claude Code

richardcocks and others added 8 commits August 21, 2026 20:03
A test that died with a non-Gleam error reported only the bare reason:
a stale filepath.beam shadowing the filepath package turned every test
in a module into an information-free "Crashed: Undef". The M:F/A that
identifies the failure was in the stacktrace's top frame, discarded by
catch_panic.

catch_panic now captures the stacktrace and returns {Reason, Stacktrace}
- deliberately the shape BEAM exit reasons already have, so one
split_crash/1 serves both the caught-panic and process-death paths.
Classification reduces the trace to a CrashSite (module, function,
arity, file/line when the frame carries them) on UnknownDetail and
ExitDetail, keeping the inspectable reason free of frame dumps.

The console reads "Crashed: Undef calling config_parser:parse/1" with an
"at file:line" line when known; TeamCity and JUnit messages carry the
same summary via describe.crash; JSONL gains site_* fields mirroring the
todo outcome's naming. JavaScript is unchanged - a JS stacktrace is an
unparsed string, so there is never a site and the fallback wording is
pinned by spec.

The playground gains crashing_config_test (undef on the BEAM, a raw
TypeError on JavaScript, where a missing module would break discovery
at import time), and the e2e suite asserts the site fields through the
subprocess JSONL stream on both targets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Crash reports from processes the tests spawned (and anything else routed
through OTP's logger) used to hit stderr asynchronously, interleaving
with test output — and in watch mode straddling the inner run's frame —
or getting lost entirely when the VM halted first.

vouch_ffi now doubles as a logger handler: it replaces the default
handler, renders each event with the formatter the default handler would
have used, and stashes the text in ETS. The runner drains the table after
the run summary and reprints everything as one stderr block, so stdout
stays machine-clean and nothing is lost at halt. If the handler cannot be
installed, the old redirect-to-stderr remains as the fallback.

take_diagnostics_matching lets the suite's deliberately-crashing specs
(linked crash, gen_server todo) assert their reports were captured and
consume them, keeping vouch's own run tail clean without racing other
tests under --parallel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
linked_undef_crash_names_call_site_test leaks a linked process that dies
with undef, so its emulator report showed up as a stray diagnostic at the
end of the vouch test run. Assert it was captured and consume it,
matching the other crash-generating specs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A process that dies under a test is already reported through the test
outcome; the BEAM crash report of the same death is a duplicate, and
for OTP actors it is sixty lines each. Keep the capture handler (it is
what stops the reports reaching stderr) and drop the end-of-run block.
take_diagnostics_matching stays so the suite can prove the reports are
captured rather than printed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Crash reports are swallowed by default; this flag prints the captured
reports as one block on stderr after the summary, for when the full BEAM
report is wanted. Warned as ineffective on the JavaScript target, like
--timeout and --parallel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@richardcocks
richardcocks merged commit c3338f7 into main Aug 21, 2026
3 checks passed
@richardcocks
richardcocks deleted the crash-site-reporting branch August 21, 2026 21:12
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.

1 participant