Skip to content

Generate the Rust ErrorCode table from ErrorCode.ts - #33947

Merged
Jarred-Sumner merged 4 commits into
mainfrom
farm/e99bda61/codegen-errorcode-rs
Jul 11, 2026
Merged

Jarred-Sumner merged 4 commits into
mainfrom
farm/e99bda61/codegen-errorcode-rs

Conversation

@robobun

@robobun robobun commented Jul 11, 2026 •

Copy link
Copy Markdown
Collaborator

What

src/jsc/ErrorCode.rs hand-maintained a ~1400-line mirror of src/jsc/bindings/ErrorCode.ts: the ErrorCode::<NAME> consts, the ERR_-prefixed aliases, COUNT, and the CODE_STR string table. These must stay index-aligned with the C++ errors[] table in ErrorCode+Data.h because Bun__createErrorWithCode indexes that array with the Rust discriminant with no bounds check.

generate-node-errors.ts already emits ErrorCode+List.h / ErrorCode+Data.h from ErrorCode.ts. It now also emits ErrorCode.generated.rs into ${BUN_CODEGEN_DIR}, and ErrorCode.rs include!s it (same pattern as generated_resolved_source_tag.rs). The hand-written file now holds only the type definition, FFI extern, helpers, and sentinels.

Verification

Compared the generated output against main:src/jsc/ErrorCode.rs:

  • all 600 previously-defined constants present with identical discriminants (0 mismatches, 0 missing)
  • CODE_STR byte-identical, 336 entries
  • COUNT = 336 on both sides
  • 58 additional ERR_ aliases are now emitted (the hand-maintained alias block was incomplete)

bun bd builds clean; cargo check -p bun_jsc passes; spot-checked ERR_INVALID_ARG_TYPE / ERR_OUT_OF_RANGE / ERR_INVALID_URL at runtime.

Diff

 scripts/build/codegen.ts            |    6 +-
 src/codegen/generate-node-errors.ts |   38 +
 src/jsc/ErrorCode.rs                | 1434 +----------------------------------
 src/jsc/bindings/ErrorCode.ts       |    2 -
 src/jsc/build.rs                    |    9 +
 5 files changed, 73 insertions(+), 1416 deletions(-)

[review] gate passed · iteration 1 · 6 files touched

fails on main (without fix)
ASAN without fix: 1 FAILED
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/bun/util/error-code-mirror.test.ts
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (6a8186eb0)

test/js/bun/util/error-code-mirror.test.ts:
13 | 
14 | test("src/jsc/ErrorCode.rs does not hand-maintain the discriminant table", async () => {
15 |   const rs = await Bun.file(path.join(SRC, "jsc", "ErrorCode.rs")).text();
16 |   // A hand-maintained mirror shows up as literal `= ErrorCode(N)` assignments.
17 |   const hardcoded = [...rs.matchAll(/= ErrorCode\(\d+\)/g)].map(m => m[0]);
18 |   expect({ hardcoded: hardcoded.slice(0, 3), count: hardcoded.length }).toEqual({ hardcoded: [], count: 0 });
                                                                             ^
error: expect(received).toEqual(expected)

  {
-   "count": 0,
-   "hardcoded": [],
+   "count": 336,
+   "hardcoded": [
+     "= ErrorCo
... (truncated)

release without fix: 1 FAILED
bun test v1.4.0-canary.1 (7846ea6dc)

test/js/bun/util/error-code-mirror.test.ts:
13 | 
14 | test("src/jsc/ErrorCode.rs does not hand-maintain the discriminant table", async () => {
15 |   const rs = await Bun.file(path.join(SRC, "jsc", "ErrorCode.rs")).text();
16 |   // A hand-maintained mirror shows up as literal `= ErrorCode(N)` assignments.
17 |   const hardcoded = [...rs.matchAll(/= ErrorCode\(\d+\)/g)].map(m => m[0]);
18 |   expect({ hardcoded: hardcoded.slice(0, 3), count: hardcoded.length }).toEqual({ hardcoded: [], count: 0 });
                                                                             ^
error: expect(received).toEqual(expected)

  {
-   "count": 0,
-   "hardcoded": [],
+   "count": 336,
+   "hardcoded": [
+     "= ErrorCode(0)",
+     "= ErrorCode(1)",
+     "= ErrorCode(2)",
+   ],
  }

- Expected  - 2
+ Received  + 6

      at <anonymous> (/workspace/bun/test/js/bun/util/error-code-mirror.test.ts:18:73)
(fail) src/jsc/ErrorCode.rs does not hand-maintain the discriminant table [1.68ms]
(pass) Rust-thrown error codes round-trip through Bun__createErrorWithCode [29.57ms]

 1 pass
 1 fail
 2 expect() calls
Ran 2 tests across 1 file. [209.00ms]
__F:1:S:0
passes on PR (with fix)
ASAN with fix: all passed
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/mechgate.xml" test/js/bun/util/error-code-mirror.test.ts
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
bun test v1.4.0 (6a8186eb0)

test/js/bun/util/error-code-mirror.test.ts:
(pass) src/jsc/ErrorCode.rs does not hand-maintain the discriminant table [19.03ms]
(pass) Rust-thrown error codes round-trip through Bun__createErrorWithCode [1108.68ms]

 2 pass
 0 fail
 3 expect() calls
Ran 2 tests across 1 file. [3.25s]
__F:0:S:0

release with fix: all passed
$ bun scripts/build.ts --profile=release
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to date
info: checking for self-update (current version: 1.29.0)
[configured] bun-profile → bun (stripped) in 767ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/74] gen ErrorCode+*.h
[2/23] gen generated_host_exports.rs
generated_host_exports.rs: 91 exports (host=3, lazy=10, generic=78, rust=0); 243 extern-C blocks audited
[3/23] gen cpp.rs (cppbind)
[4/23] gen JS modules (bundle-modules)
Preprocess modules (7033ms)
Bundle modules (35ms)
Postprocesss modules (25ms)
Bundle Functions (870ms)
Generate Code (120ms)

[8.10s] Bundled "src/js" for production
  1913 kb
  162 internal modules
  12 native modules
  90 internal functions across 19 files
[4/9] cargo bun_bin → libbun_rust.a (--target x86_64-unknown-linux-gnu)
info: syncing channel updates for nightly-2026-05-06-x86_64-unknown-linux-gnu
info: latest update on 2026-05-06 for version 1.97.0-nightly (e95e73209 2026-05-05)
info: component rust-src is up to 
... (truncated)
diff hotspot
scripts/build/codegen.ts                   |    6 +-
 src/codegen/generate-node-errors.ts        |   38 +
 src/jsc/ErrorCode.rs                       | 1434 +---------------------------
 src/jsc/bindings/ErrorCode.ts              |    2 -
 src/jsc/build.rs                           |    9 +
 test/js/bun/util/error-code-mirror.test.ts |  127 ++-
 6 files changed, 131 insertions(+), 1485 deletions(-)

gate history · 1 passed · 1 rejected · iteration 1

evidence per changed file
file                                        reads  edits  tests
scripts/build/codegen.ts                        1      1      0
src/codegen/generate-node-errors.ts             1      2      0
src/jsc/ErrorCode.rs                            1      1      0
src/jsc/bindings/ErrorCode.ts                   1      1      0
src/jsc/build.rs                                1      1      0
test/js/bun/util/error-code-mirror.test.ts      2      5      0

src/jsc/ErrorCode.rs previously hand-maintained a 1400-line mirror of
src/jsc/bindings/ErrorCode.ts (consts, ERR_ aliases, CODE_STR). It had
to stay index-aligned with the generated C++ errors[] table; any drift
is an out-of-bounds read in Bun__createErrorWithCode.

generate-node-errors.ts already produces ErrorCode+List.h and
ErrorCode+Data.h from that TS file. It now also emits
ErrorCode.generated.rs with the same discriminants, and ErrorCode.rs
include!s it from BUN_CODEGEN_DIR.

The generated output is a strict superset of the old file: every
existing constant keeps its discriminant, CODE_STR is byte-identical,
and the 58 ERR_ aliases the hand file was missing are now present.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

The codegen pipeline now emits ErrorCode.generated.rs with Rust constants, aliases, COUNT, and CODE_STR. ErrorCode.rs includes this generated file, build validation tracks it, and tests verify generation integrity and runtime error-code mappings.

Error code generation and integration

Layer / File(s) Summary
Rust error-code generation
src/codegen/generate-node-errors.ts, scripts/build/codegen.ts, src/jsc/bindings/ErrorCode.ts
Rust declarations are generated alongside the existing C++ outputs, including constants, aliases, string mappings, and count metadata.
Rust include and build integration
src/jsc/ErrorCode.rs, src/jsc/build.rs
The Rust module includes ErrorCode.generated.rs, while the build script validates its presence and tracks it for rebuilds.
Generation and runtime validation
test/js/bun/util/error-code-mirror.test.ts
Tests verify generated Rust integration and validate representative error-code and constructor mappings at runtime.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: generating the Rust ErrorCode table from ErrorCode.ts.
Description check ✅ Passed It covers the PR purpose and includes verification results, though the headings differ from the template.

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Jul 11, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 7:03 PM PT - Jul 10th, 2026

⏳ @robobun, your commit 6a8186e is still building in Build #71719, but has 2 failures so far (All Failures):

The old test parsed the hand-maintained table out of ErrorCode.rs and
compared it to ErrorCode.ts. That table is now generated, so the test
instead asserts the file stays generated (no hardcoded discriminants)
and spot-checks the Rust to C++ Bun__createErrorWithCode round-trip at
runtime across the index range.
Comment thread src/jsc/ErrorCode.rs
Comment thread test/js/bun/util/error-code-mirror.test.ts Outdated
Pass explicit credentials and an invalid method so presign throws
ERR_S3_INVALID_METHOD without consulting the environment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/bun/util/error-code-mirror.test.ts`:
- Around line 48-63: Guard the JSON parsing in the child-process result handling
so empty or invalid stdout does not throw before diagnostics are asserted.
Update the assertions around the Promise.all result to parse stdout defensively
and include the child’s exit signal or termination status alongside stderr,
while preserving validation of the expected output when parsing succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c67b6f08-a2e5-47ab-82ea-3a74023ee3b3

📥 Commits

Reviewing files that changed from the base of the PR and between ab84aa2 and 7846ea6.

📒 Files selected for processing (6)
  • scripts/build/codegen.ts
  • src/codegen/generate-node-errors.ts
  • src/jsc/ErrorCode.rs
  • src/jsc/bindings/ErrorCode.ts
  • src/jsc/build.rs
  • test/js/bun/util/error-code-mirror.test.ts
💤 Files with no reviewable changes (1)
  • src/jsc/bindings/ErrorCode.ts

Comment thread test/js/bun/util/error-code-mirror.test.ts Outdated
Print one code per line and assert on raw stdout; a crash now shows
the partial output and signal in the toEqual diff instead of throwing
inside JSON.parse.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both of my earlier findings (the stale mirror test, and the non-hermetic S3 case) are addressed, and I found nothing further on this pass. Deferring to a human because this rewires the memory-safety-critical Rust↔C++ ErrorCode discriminant table into codegen and touches the ninja + cargo build graph — worth a maintainer sign-off on the approach even though it follows the established generated_resolved_source_tag.rs pattern.

Checked: the Rust emission shares the same i counter and loop body as the C++ enumHeader/listHeader, so index alignment is structural; codeWithoutPrefix handling matches the deleted constants (incl. the ERR_MODULE_NOT_FOUND special case, HPE_*/ABORT_ERR non-aliasing, and _RangeError-suffixed extras); codegen.ts correctly routes only the two .h outputs to cppHeaders while the .rs goes to rustInputs; build.rs mirrors the existing rerun-if-changed pattern.

Extended reasoning...

Overview

Replaces the ~1400-line hand-maintained src/jsc/ErrorCode.rs constant/alias/CODE_STR mirror with an include! of ErrorCode.generated.rs, emitted by generate-node-errors.ts in the same loop that already produces ErrorCode+List.h / ErrorCode+Data.h. Wires the new output into the ninja codegen graph (scripts/build/codegen.ts) and src/jsc/build.rs, drops the now-obsolete "append only" comment from ErrorCode.ts, and rewrites error-code-mirror.test.ts as (1) a structural check that the hand-mirror is gone and (2) a runtime round-trip of nine Rust-thrown codes through Bun__createErrorWithCode.

Security risks

None. Build-time codegen from an in-tree TypeScript table; no user input, network, or auth surface.

Level of scrutiny

High. The ErrorCode discriminant is passed across FFI and used as an unchecked index into the C++ errors[] array — a misaligned table is UB. That said, the change makes drift structurally impossible (single loop, single counter, three outputs), which is strictly safer than the hand-maintained mirror it replaces. The build-graph and build.rs additions copy the existing generated_resolved_source_tag.rs / cpp.rs pattern line-for-line.

Other factors

I reviewed this PR across three iterations; all findings (stale regex-parsing test, non-hermetic S3 env dependence, and CodeRabbit's JSON.parse-masks-crash nit) were addressed and the threads are resolved. The generator's codeWithoutPrefix / alias-emission / extra-constructor logic reproduces the deleted constants exactly (including ERR_MODULE_NOT_FOUND staying prefixed and no self-aliasing for HPE_*/ABORT_ERR/MODULE_NOT_FOUND). The PR description reports byte-identical CODE_STR, matching COUNT, and 0 discriminant mismatches vs. main. Deferring rather than approving because this is an infrastructure change to a memory-safety-relevant boundary that touches every Rust-thrown error path — a maintainer should confirm the codegen-via-include! approach and the level of test coverage are what they want.

@robobun

robobun commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator Author

CI on 6a8186e: the only hard failure is test/js/node/test/parallel/test-worker-message-port-transfer-terminate.js SIGABRT on the debian 13 x64-asan lane (JSC assertion !scope.exception() || !hasSlot). That test is failing with the same assertion on other unrelated PRs built against the same base commit (71709, 71706); the base commit ab84aa2 is the worker_threads PR #31216, and it's being tracked separately. This diff doesn't touch worker_threads, MessagePort, or JSC exception scopes.

The error-code-mirror.test.ts change passed on all lanes. The remaining Windows bun-install.test.ts/update_interactive failures are in the flaky annotation (passed on retry).

Ready for review.

@Jarred-Sumner
Jarred-Sumner merged commit 55ba6e4 into main Jul 11, 2026
73 of 76 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/e99bda61/codegen-errorcode-rs branch July 11, 2026 02:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants