Skip to content

fix: surface Rust compositor panics to error tracking - #29

Draft
posthog[bot] wants to merge 3 commits into
mainfrom
posthog-self-driving/fixcompositor-surface-rust-panic-3d8545
Draft

fix: surface Rust compositor panics to error tracking#29
posthog[bot] wants to merge 3 commits into
mainfrom
posthog-self-driving/fixcompositor-surface-rust-panic-3d8545

Conversation

@posthog

@posthog posthog Bot commented Aug 1, 2026

Copy link
Copy Markdown

Problem

  • A compositor Rust panic reaches error tracking as a bare, stackless RuntimeError: unreachable — no message, no source location — so we can't tell which panic fired. Diagnosability hole in the core rendering path; it blocks triage of the whole class of WASM crashes.
  • The panic latches crashed = true, permanently disabling the preview for the rest of that editing session.
  • Several worker entry points (resize, loadFont, uploadBitmap, uploadLut, flush, and siblings) call markCrashed without rethrowing, so those panics never cross the comlink boundary and are never reported at all — the true crash rate may be higher than what we see.

Changes

  • Forward the panic message across the WASM boundary. Replace the console-only console_error_panic_hook with a custom std::panic::set_hook that records each panic's payload + Rust file:line:col into a thread-local (still logging to console), exposed to JS as a take_last_panic() export / takeLastCompositorPanic() helper. Reading it is safe after a trap — it only touches a thread-local, not GPU state.
  • Report instead of swallow. The worker recovers that message on the first crash and rethrows an enriched CompositorCrashedError whose message carries the real Rust location. Every entry point now rethrows on its first failure; later calls short-circuit on the crash latch, so at most one error is surfaced per crash.
  • Harden the likely panic sites into CompositorError returns / panic-free matches instead of unwrap():
File Site Before → After
compositor.rs ensure_render_texture is_none() || unwrap()match
compositor.rs render_frame_to_pixels render_texture/readback_buffer unwrap()RenderStateNotInitialized
texture.rs upload_bitmap textures.get().unwrap()TextureNotFound
text.rs highlight path bind style + indices together, no unwrap()

Risk

  • The newly-throwing void entry points now reject across comlink on first crash. The instance is already dead at that point, so this changes a silent dead preview into a reported one — which is the goal. Only the first failing call throws.

Testing

  • pnpm --filter @tooscut/render-engine test (135 passing) and oxlint on the changed TS. The Rust/WASM build requires the toolchain (not available in this environment); the Rust changes are mechanical (unwrap()?/match, new panic hook) and compile-checked by CI.

Agent context

  • Confirmed no competing PR/branch/issue; PR fix: put COEP on /assets so the compositor worker loads in production #26 (COEP on /assets) is unrelated.
  • Considered reporting from inside the worker directly, but the worker has no PostHog instance — rethrowing across comlink (as renderFrame already did) is what got the one observed event into error tracking, so extending that to the swallowing paths is the minimal, consistent fix.

Created with PostHog Desktop from this inbox report.

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tooscut Ready Ready Preview Aug 3, 2026 12:51am
tooscut-docs Ready Ready Preview Aug 3, 2026 12:51am

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

posthog Bot and others added 2 commits August 2, 2026 17:38
A Rust panic in the WASM compositor traps as a stackless `RuntimeError:
unreachable`, reaching error tracking with no message or source location.

- Replace the console-only panic hook with a custom `std::panic::set_hook`
  that records the panic payload + Rust file:line into a thread-local,
  exposed to JS via a `take_last_panic()` export.
- Have the compositor worker recover that message and rethrow an enriched
  `CompositorCrashedError`, and report (rather than silently swallow) crashes
  in the void-returning entry points (resize/loadFont/uploadBitmap/uploadLut/
  flush and siblings).
- Harden the specific unguarded unwraps that can panic into proper
  `CompositorError` returns.

Generated-By: PostHog Code
Task-Id: 14f4c22c-05c7-4b6c-b385-a67bdaf5e537
… dep

Cargo.toml already dropped the dependency; Cargo.lock wasn't updated to
match because the PR author couldn't run the Rust toolchain locally.
mohebifar added a commit that referenced this pull request Aug 3, 2026
PostHog's autonomous bug-fixing agent (PR #29) opens PRs from the same
posthog[bot] GitHub App identity every time, and can't itself click through
the CLA signing flow. Allowlisting it alongside the other bot committers
(dependabot, renovate, github-actions) unblocks the cla check on that PR
and any future ones from the same integration.
resize() worker-side used to swallow a crash silently (markCrashed without
rethrowing) — this PR's whole point is to stop that, and it now correctly
rethrows like every other entry point. But compositor-api.ts's resize()
wrapper fire-and-forgets its call (void enqueue(...), no caller awaits a
resize) and had no .catch, so a crash during resize would surface as an
unhandled promise rejection in the console — the crash itself still gets
reported correctly via reportCrash()/onCrash() (reportingCall does that
before rethrowing), this was purely a stray rejection on top of that.
@mohebifar
mohebifar force-pushed the posthog-self-driving/fixcompositor-surface-rust-panic-3d8545 branch from 23fbf7c to 0c9db75 Compare August 3, 2026 00:41
@mohebifar

Copy link
Copy Markdown
Owner

Reviewed and addressed the open items from review:

  • Rebased onto current main. The branch had forked from the commit right before fix: WASM panic on readback after a resolution change during playback #28 (the readback-buffer resolution-change fix) landed, so it was several fixes behind — this now includes them.
  • Regenerated Cargo.lock (chore: regenerate Cargo.lock for the removed console_error_panic_hook dep) — Cargo.toml dropped the dependency but the lockfile was never updated to match, since the original run couldn't use the Rust toolchain locally.
  • Fixed the resize() unhandled-rejection gap (fix: catch the now-possible resize() rejection) — compositor-api.ts's resize() fire-and-forgets its worker call with no .catch. Since this PR makes worker-side resize throw on crash instead of swallowing it (which is the whole point of the PR), that surfaced as an unhandled promise rejection in the console. The crash itself was still being reported correctly via reportCrash/onCrash either way — this was just a stray rejection on top of that.

Verified end-to-end after these changes: cargo check --target wasm32-unknown-unknown (0 errors), pnpm build:wasm, full render-engine build, typecheck/lint/knip/format, 141 Node tests, and 205/205 browser tests (including the readback-buffer regression test from #28).

posthog[bot] has also been added to the CLA allowlist in #33 so the cla check here should pass once that merges.

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