fix(sql): don't crash in debug builds when bun:sql module load throws - #34831
fix(sql): don't crash in debug builds when bun:sql module load throws#34831robobun wants to merge 2 commits into
Conversation
The Bun.sql / Bun.SQL lazy property callbacks call requireId() to load the bun:sql internal module. If that evaluation throws (for example because user code clobbered a global the module reads at top level), the debug-only reportUncaughtExceptionAtEventLoop() call would run the full uncaught-exception machinery. That path can clear the pending exception (via Bun__handleUncaughtException -> tryClearException), so the following RETURN_IF_EXCEPTION doesn't fire and we dereference a null getObject() result. Drop the debug block so the exception propagates to the caller the same way it does in release builds.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
Comment |
|
Updated 6:33 AM PT - Jul 20th, 2026
@autofix-ci[bot], your commit b9f302c is building: |
|
This PR may be a duplicate of:
🤖 Generated with Claude Code |
|
Closing as a duplicate of #33211. That PR has the complete fix. Mine only removed the debug-only globalThis.Symbol = "i";
try { Bun.SQL; } catch {} // throws, reifies empty JSValue into the slot
typeof Bun.SQL; // segfault on release and debug#33211 handles both by clearing the exception and reifying |
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||
|
|
||
| expect(stdout.trim()).toBe("caught=2"); | ||
| expect(stderr).toBe(""); |
There was a problem hiding this comment.
🟡 Per REVIEW.md, avoid asserting stderr is exactly empty — ASAN/debug builds can emit benign diagnostics that make this flake in CI. The stdout === "caught=2" and exitCode === 0 assertions already prove the process didn't crash and both exceptions were caught; consider dropping this line or asserting a combined { stdout, exitCode } snapshot instead.
Extended reasoning...
What the issue is
Line 29 asserts expect(stderr).toBe(""). REVIEW.md's Tests reviewers reject section explicitly calls this out:
Never assert stderr is exactly empty (ASAN/debug builds emit benign warnings); assert a combined
{ stdout, stderr, exitCode }object.
Why it can flake
The test spawns bunExe() with bunEnv. bunEnv sets BUN_DEBUG_QUIET_LOGS=1, which suppresses Bun's own scoped debug loggers, but it does not suppress ASAN/UBSAN runtime diagnostics or other libc/loader warnings that can appear on stderr in the debug+ASAN CI lanes. When such a benign line appears, stderr becomes non-empty and this assertion fails even though the behavior under test — the catchable TypeError propagating out of the Bun.sql/Bun.SQL lazy getters — is working correctly.
Step-by-step
- CI runs the debug+ASAN lane.
- The spawned child prints
caught=2\nto stdout and exits 0 — the fix is working. - ASAN (or another runtime component) writes a benign one-line warning to stderr.
expect(stdout.trim()).toBe("caught=2")passes.expect(stderr).toBe("")fails → spurious red CI.
Why the existing assertions already suffice
The purpose of this test is to prove the process doesn't crash and that the module-load exception propagates as a catchable JS error. Both facts are fully established by:
stdout.trim() === "caught=2"→ bothtry { Bun.sql }andtry { Bun.SQL }threw and were caught.exitCode === 0→ no crash, no abort, no uncaught exception.
The stderr === "" assertion adds no additional signal — it only introduces flake risk.
Suggested fix
Either drop line 29 entirely, or use the repo-conventional combined-object snapshot:
expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "caught=2", exitCode: 0 });(If you want stderr visible in failure output for debugging, include it in the object but don't assert it's exactly empty.)
Severity
Marking as nit: this is a test-hygiene / potential-flake concern, not a correctness bug in the fix itself. The pattern also appears widely elsewhere in the test suite, so it's not uniquely blocking here — just worth aligning with the stated guideline while the file is new.
Fuzzilli found a crash (fingerprint
281fcb3ecf3202d4) where accessingBun.SQLorBun.sqlafterglobalThis.Symbolwas replaced with a non-function would crash the process in debug/ASAN builds instead of throwing.What
The
Bun.sql/Bun.SQLlazy property callbacks inBunObject.cppcallrequireId()to load thebun:sqlinternal module. In debug builds, if that evaluation threw, a#if BUN_DEBUGblock calledreportUncaughtExceptionAtEventLoop()before theRETURN_IF_EXCEPTIONcheck.reportUncaughtExceptionAtEventLooproutes throughBun__reportUnhandledError->uncaught_exception->Bun__handleUncaughtException, which can clear the pending VM exception (viatryClearExceptionon the_fatalExceptionlookup). With the exception cleared,RETURN_IF_EXCEPTIONdoesn't fire andsqlValue.getObject()->get(...)dereferences a null pointer.Release builds were unaffected since the block is
#if BUN_DEBUGonly.How
Remove the debug-only
reportUncaughtExceptionAtEventLoopcalls. The exception now propagates to the caller the same way in debug and release builds.Repro
Before:
member call on null pointer of type 'JSC::JSCell'(UBSAN) orassertNoException(exception scope check)After: throws
TypeError: Symbol is not a functionwhich is catchable