Skip to content

bake: validate app.bundlerOptions and sub-options are objects#30422

Closed
robobun wants to merge 2 commits into
mainfrom
farm/6970e26e/bake-bundler-options-validation
Closed

bake: validate app.bundlerOptions and sub-options are objects#30422
robobun wants to merge 2 commits into
mainfrom
farm/6970e26e/bake-bundler-options-validation

Conversation

@robobun

@robobun robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Fixes a debug assertion crash in Bun.serve({ app: ... }) when app.bundlerOptions, app.bundlerOptions.{server,client,ssr}, or their .minify option is a non-object primitive (number, string, boolean).

JSValue.get() asserts that its target is an object, but BuildConfigSubset.fromJS and UserOptions.fromJS called it on values obtained via getOptional(..., JSValue) without first checking they were objects.

Also fixes minify: false falling through to the object-property read path (which would then assert), since the previous check was isBoolean() and asBoolean().

Repro

Bun.serve({ app: { bundlerOptions: { client: 2 } } });
// or
Bun.serve({ app: { bundlerOptions: 5 } });
// or
Bun.serve({ app: { bundlerOptions: { client: { minify: 5 } } } });
// or
Bun.serve({ app: { bundlerOptions: { client: { minify: false } } } });

Before: panic(main thread): reached unreachable code at JSValue.getbun.debugAssert(target.isObject()).

After: throws TypeError: 'app.bundlerOptions.client' must be an object (etc).

How did you verify your code works?

Added test/bake/bundler-options-validation.test.ts covering all three levels of non-object inputs plus the minify: false path.

Found by Fuzzilli (fingerprint 9aa9c17813ebf2b4).

Previously, passing a non-object value for app.bundlerOptions,
app.bundlerOptions.{server,client,ssr}, or their .minify option would
trigger a debug assertion in JSValue.get() instead of throwing a
proper JS TypeError.

Also fixes minify: false falling through to the object-property path.
@robobun

robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 11:14 PM PT - May 8th, 2026

@robobun, your commit 504a387 has 1 failures in Build #52983 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30422

That installs a local version of the PR into your bun-30422 executable, so you can run:

bun-30422 --bun

@github-actions github-actions Bot added the claude label May 9, 2026
@coderabbitai

coderabbitai Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 05402ba9-89d5-458d-aa23-0625be41b17f

📥 Commits

Reviewing files that changed from the base of the PR and between 7eaad1f and 504a387.

📒 Files selected for processing (1)
  • test/bake/bundler-options-validation.test.ts

Walkthrough

This PR refines bundler options validation for Bun.serve. The BuildConfigSubset.fromJS function now accepts a compile-time property name to generate property-qualified error messages. UserOptions.fromJS calls it with explicit labels for server, client, and ssr. Minify boolean values now set all three minify fields uniformly rather than only when true. Tests cover non-object bundler options, nested field validation, minify type requirements, and edge cases.

Changes

Bundler Options Validation with Property-Qualified Errors

Layer / File(s) Summary
API Signature and Integration
src/bake/bake.zig
BuildConfigSubset.fromJS now accepts a comptime property_name parameter. UserOptions.fromJS calls BuildConfigSubset.fromJS with explicit property labels ("server", "client", "ssr") for each nested bundler option subset, enabling targeted error messages.
Minify Field Parsing and Validation
src/bake/bake.zig
minify parsing sets all three minify fields (minify_syntax, minify_identifiers, minify_whitespace) to the same boolean value. Non-boolean/non-object minify values throw property-qualified errors that include the full path api_name.bundlerOptions.<property_name>.minify.
Validation Test Suite
test/bake/bundler-options-validation.test.ts
New test suite validates that bundlerOptions and nested fields (server, client, ssr) must be objects; minify must be boolean or object; error messages include property paths; and edge cases like self-referencing options do not crash.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding validation for app.bundlerOptions and its sub-options to ensure they are objects.
Description check ✅ Passed The description covers both required template sections: 'What does this PR do?' explains the fix and includes repro cases, and 'How did you verify your code works?' documents the test coverage.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@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/bake/bundler-options-validation.test.ts`:
- Around line 16-54: Three parameterized tests use test.each(...) but must
follow guidelines and be converted to describe.each(...) with inner test(...)
blocks; replace each test.each(["server","client","ssr"] as const)(...) (the
first block that asserts bundlerOptions.%s is an object, the second block that
asserts bundlerOptions.%s.minify type, and the third block labeled "accepts
minify: false without crashing for %s") with describe.each([...])('%s', key => {
test("throws when ...", () => { ... }) }) (preserving the existing expectation
logic and messages and keeping Bun.serve invocations and error assertions
unchanged). Ensure the describe.each label and each inner test name mirrors the
original message so test output stays the same.
🪄 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: 48407c21-cbda-48db-8a1f-03fba20d71c8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d0d86b and 7eaad1f.

📒 Files selected for processing (2)
  • src/bake/bake.zig
  • test/bake/bundler-options-validation.test.ts

Comment on lines +16 to +54
test.each(["server", "client", "ssr"] as const)("throws when bundlerOptions.%s is not an object", key => {
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: 2 } as any } } as any)).toThrow(
`'app.bundlerOptions.${key}' must be an object`,
);
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: "x" } as any } } as any)).toThrow(
`'app.bundlerOptions.${key}' must be an object`,
);
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: true } as any } } as any)).toThrow(
`'app.bundlerOptions.${key}' must be an object`,
);
});

test.each(["server", "client", "ssr"] as const)(
"throws when bundlerOptions.%s.minify is not a boolean or an object",
key => {
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: { minify: 5 } } as any } } as any)).toThrow(
`'app.bundlerOptions.${key}.minify' must be a boolean or an object`,
);
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: { minify: "yes" } } as any } } as any)).toThrow(
`'app.bundlerOptions.${key}.minify' must be a boolean or an object`,
);
},
);

test("does not crash with self-referencing bundlerOptions and non-object sub-options", () => {
const v2: any = {};
v2.client = 2;
v2.bundlerOptions = v2;
expect(() => Bun.serve({ app: v2 } as any)).toThrow("'app.bundlerOptions.client' must be an object");
});

test.each(["server", "client", "ssr"] as const)("accepts minify: false without crashing for %s", key => {
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: { minify: false } } as any } } as any)).toThrow(
"'app' is missing 'framework'",
);
expect(() => Bun.serve({ app: { bundlerOptions: { [key]: { minify: true } } as any } } as any)).toThrow(
"'app' is missing 'framework'",
);
});

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify parameterized test style usage in this file
rg -nP '\btest\.each\s*\(' test/bake/bundler-options-validation.test.ts

Repository: oven-sh/bun

Length of output: 332


Convert test.each(...) blocks to describe.each(...) + inner test(...) for guideline consistency.

Per coding guidelines, parameterized tests must use describe.each(). Convert all three test.each() blocks at lines 16, 28, and 47 to the pattern shown below:

♻️ Example refactor pattern
- test.each(["server", "client", "ssr"] as const)("throws when bundlerOptions.%s is not an object", key => {
-   expect(() => Bun.serve({ app: { bundlerOptions: { [key]: 2 } as any } } as any)).toThrow(
-     `'app.bundlerOptions.${key}' must be an object`,
-   );
-   // ...
- });
+ describe.each(["server", "client", "ssr"] as const)("bundlerOptions.%s", key => {
+   test("throws when value is not an object", () => {
+     expect(() => Bun.serve({ app: { bundlerOptions: { [key]: 2 } as any } } as any)).toThrow(
+       `'app.bundlerOptions.${key}' must be an object`,
+     );
+     // ...
+   });
+ });
🤖 Prompt for 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.

In `@test/bake/bundler-options-validation.test.ts` around lines 16 - 54, Three
parameterized tests use test.each(...) but must follow guidelines and be
converted to describe.each(...) with inner test(...) blocks; replace each
test.each(["server","client","ssr"] as const)(...) (the first block that asserts
bundlerOptions.%s is an object, the second block that asserts
bundlerOptions.%s.minify type, and the third block labeled "accepts minify:
false without crashing for %s") with describe.each([...])('%s', key => {
test("throws when ...", () => { ... }) }) (preserving the existing expectation
logic and messages and keeping Bun.serve invocations and error assertions
unchanged). Ensure the describe.each label and each inner test name mirrors the
original message so test output stays the same.

@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. bake: validate bundlerOptions values are objects before property access #30125 - Also validates bundlerOptions and sub-options are objects in the same UserOptions.fromJS/BuildConfigSubset.fromJS functions in src/bake/bake.zig, and fixes the same minify: false boolean logic bug
  2. Validate app.bundlerOptions types in Bun.serve #30402 - Also adds isObject() validation for app.bundlerOptions and its sub-options in the same bake.zig code path, with equivalent tests

🤖 Generated with Claude Code

@robobun

robobun commented May 9, 2026

Copy link
Copy Markdown
Collaborator Author

Duplicate of #30125 which already has the same fix.

@robobun robobun closed this May 9, 2026
@robobun robobun deleted the farm/6970e26e/bake-bundler-options-validation branch May 9, 2026 06:13
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.

1 participant