Repository navigation
Conversation
…y if every layout is `bun build --app` failed with `TypeError: No default value` for a route that has a layout. In `renderRoutesForProdStatic` the `.then` parameter shadowed the file index, so the module namespace object was the key into `loadedModules`. The callback now stores the module under the file index. The `no_client` flag of a route came from the page file alone. Each layout is a separate server entry point with its own classification, so a static page under a layout that imports a client component got no client entry point. The flag is now folded over the page and every layout above it, in the walk that counts those layouts.
|
Updated 4:06 AM PT - Oct 28th, 2026
❌ @robobun, your commit ba5ba81 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 47150That installs a local version of the PR into your bun-47150 --bun |
|
Status: ready for review. The diff is green on every CI lane that ran. How I reproduced the bug:
CI for ba5ba81 (build 134300):
PR: #47150 |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Aggregate page and layout classifications src/runtime/bake/production/mod.rs |
The route flag starts with the page classification, combines the classification of each present layout, and passes the aggregate value to TypeAndFlags. |
Production-build coverage test/bake/dev/production.test.ts |
Tests check layout chains, getParams-derived routes, prerendered styles, and module counts with server components enabled and disabled. |
Module loader callback naming
| Layer / File(s) | Summary |
|---|---|
Rename callback parameters src/js/builtins/Bake.ts |
The callback parameters change from x to id and mod. Loading and caching behavior remain unchanged. |
Suggested reviewers: jarred-sumner
Priority:
Merge Risk: 
Minimal · up to ba5ba
-
Routes with layouts now use the intended module and classification paths. No merge-blocking risk was identified.
Pre-merge checks: 
4
-
Full details: Title check
-
Explanation
The title clearly identifies both primary changes: prerendering routes with layouts and requiring all layouts to be static. The final wording is grammatically incomplete, but the intended change is clear.
Full details: Description check
-
Explanation
The description thoroughly explains the problem, fix, verification, scope, limitations, and related issues. It does not use the template headings exactly, but it provides the required information in equivalent sections.
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands.
Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
-
- Commit to this branch
- Create a new PR
There was a problem hiding this comment.
Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.
Beyond the inline findings, I also checked that the new fully_static walk in build_with_vm (src/runtime/bake/production/mod.rs:992-1011) visits exactly the same layout chain the fill pass loads into file_list (route.file_layout, then each parent's), so the flag covers every module the route actually renders; and that src/js/builtins/Bake.ts has no ahead-of-time/ twin needing the same rename.
Extended reasoning...
The change is a two-line variable-shadowing fix in the Bake prerender builtin, a small Rust accumulator that ANDs each layout's static classification into the route's flag, and a new test block; it touches no auth, crypto, or input-parsing surface. Two confirmed findings are posted inline, so a human look is already signalled; this note records only what else was verified.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟣
src/js/builtins/Bake.ts— pre-existing: framework users whose prerender fails for a dynamic route under a layout now get exit 0 and a missing page instead of the loud exit 1 the base gives every layout build. In the{ pages }arm at Bake.ts:176 the arrow has a block body, so the promise from callRouteGenerator is dropped and Promise.all resolves on undefined. The iterator arms only await a result that is still pending, so a synchronously throwing prerender (already-rejected promise) is dropped too. Fix: propagate every rejection from callRouteGenerator to the render promise on all three arms, e.g. return the promise in the map and await result unconditionally. Same pattern at 3 sites (Bake.ts:162, Bake.ts:169, Bake.ts:176).Why this was flagged
A route with params under a layout, whose getParams returns
{ pages: [...] }, and whose prerender rejects for one of the params. On the base branch the build never reaches this arm for any route with a layout: renderRoutesForProdStatic threwTypeError: No default valueat the shadowedloadedModules[x]and exited 1. After this PR the route reaches Bake.ts:174-178, wherepages.map(params => { callRouteGenerator(...); })returns undefined for each entry, soawait Promise.all(...)resolves immediately and the rejected doGenerateRoute promise is an unhandled rejection. build_with_vm then seesUnwrapped::Fulfilledat src/runtime/bake/production/mod.rs:1184, printsdone, and returns Ok(()), so the caller at mod.rs:223 leaves exit_code 0 with that page's files missing from dist. The iterator arms at Bake.ts:162 and Bake.ts:169 gate on$isPromisePending(result); when the framework prerender throws synchronously, doGenerateRoute returns an already-rejected promise, the gate is false, and the rejection is dropped the same way.Verification: pre-existing (the base already fails the same way for routes without layouts; this PR newly routes layout builds onto the same arm). In src/js/builtins/Bake.ts:174-178 the arrow has a block body with no
return, soPromise.allresolves immediately and the promise fromdoGenerateRouteis dropped.build_with_vmreturnsOk(()), so the build exits 0 with the page's files never written.
|
The additional finding is correct: a failed
|
Problem
bun build --appexits 1 withTypeError: No default valuewhen a route has a layout. Thereactframework has layouts on.renderRoutesForProdStatic(src/js/builtins/Bake.ts:132): the.thenparameter shadows the file index, so a module namespace object is the key intoloadedModules.no_clientflag reads only the page (src/runtime/bake/production/mod.rs:1115). A client component in a layout gets no script.Fix
Bake.ts: the callback stores the module under the file index. The parameter typeobjectmakestscreject it as a key.production/mod.rs: the flag is the AND over the page and each layout above it. A route renders all of them.test/bake/dev/production.test.ts, 4 new cases. All fail on main. With only theBake.tschange, 1 fails.Background
"use client"file. A fully static route getsmodules: [], so no client<script>.--appneeds a canary build orBUN_FEATURE_FLAG_EXPERIMENTAL_BAKE=1. No user reported this.Downsides
build_with_vm: +63 bytes.bun build --appwhen two files match the same route pattern #47146), a failed prerender (bake: fail the build when a component throws during static prerender #33216) and anonResolveplugin (bake: bind each production entry point to its graph node, not to its position #46804).Notes
Source and reach.
bun build --appfound the failure. bake: prerender routes that have layouts #38336 found it before, on 2026-08-14. No user reported it and no issue exists.--appis refused unless the build is canary or debug, orBUN_FEATURE_FLAG_EXPERIMENTAL_BAKEis set (src/bun_core/feature_flags.rs:114,src/runtime/cli/Arguments.rs:2420).Repro. A framework with no dependencies:
Without
routes/_layout.tsthe build exits 0. With this PRdist/index.htmlishome layouts=_layout.ts.Proof runs.
TypeError: No default value. A debug build of that commit fails the same way./blog/admin,/docs,/docs/guideand/docs/introget 0 modules. A debug build without thefully_static &=line fails the same case.--timeout 180000. The React cases need more than 5 s on a debug build, on main too.tsc --noEmit -p src/js/tsconfig.json: no error inBake.ts. WithloadedModules[mod] = modit reportsBake.ts(134,97): error TS2538: Type 'object' cannot be used as an index type.reactframework, run by hand with the two cases of e47140f (a root layout and a nested layout, a client component in a page and in a layout): main fails both with the TypeError. Rename only passes the first and fails the second, because/docsand/docs/introhave no<script type="module">. This PR passes both.framework-router,app-options,dev-and-prod,dev/ssg-pages-routeranddev/react-spaintest/bake(82 pass).What the new cases cover. Both use a framework with no dependencies that writes, for each page, the files, the count of client modules and the stylesheets that the build passed.
nextjs-pagesandnextjs-app-ui: a root layout, a nested layout that two pages share, a page two levels below a layout, and a dynamic route. Each page gets its layouts with the inner-most first.getParamsgets the same layouts. Each page gets the stylesheet that the root layout imports.Still wrong for a build with a layout. Main fails each of these builds with the TypeError. With this PR they reach a defect that main already has for routes with no layout. I ran each one on release builds of main and of this PR.
_layout.tsbeside_layout.js): the build printserror: Multiple layout matching the same route pattern is ambiguous, keeps one layout and exits 0. Two pages for one pattern do the same on main. bake: failbun build --appwhen two files match the same route pattern #47146 fails the build for every collision.onResolveplugin that passes on a route file: pages are built from the modules of other files, exit 0. Main does this for routes with no layout. bake: bind each production entry point to its graph node, not to its position #46804 binds each entry point to its own output.prerenderthat fails for a route fromgetParams: the build prints the error anddone, and exits 0 without the page. This happens under{ pages }, and under an iterator whenprerenderis not async. Main does this for routes with no layout. bake: fail the build when a component throws during static prerender #33216 changes the{ pages }arm.meta.stylestwice. The array also has a hole for each stylesheet of the app that the route does not use. The hole is on main for routes with no layout. 771bda2 in bake: consolidate the open robobun dev-server, HMR runtime, production build and router fixes #39488 removes the duplicate.StaticRouteVisitor.rs:27). Main does this for pages."use client"file still exits 1 withCannot create a string longer than 2147483647 characters, with or without a layout, as on main. bake: bind each production entry point to its graph node, not to its position #46804 names the cause in its error.Other open PRs on these lines.
_layoutfails and that bake: consolidate the open robobun dev-server, HMR runtime, production build and router fixes #39488 has the fix.git merge-treewith its head 4d0d06e is clean. The PR that lands second can fold overrouter.page_files(route)and can add a plugin case with a page under a layout.bun build --appwhen two files match the same route pattern #47146 (route collisions):git merge-treewith its head 977d462 is clean.Relation to the Bake rework. This PR repairs the prerender step of
bun build --app, the only production build on main. #46018 (bun bake build) closed on 2026-10-21 "while the Bake design is reworked". An open question asks whether the prerender step inbuild_with_vmstays: #46804 (comment). If it does not stay, this PR can close with nothing left over.Designs considered.
&=sites in a loop body that exists three times, and bake: bind each production entry point to its graph node, not to its position #46804 rewrites that loop. The walk that counts the layouts visits each one once.meta.modulesfor every server components framework and removesStaticRouteVisitor. It needs a maintainer's decision.no-shadowrule: it flags 175 sites insrc/js, and one of them is a bug.Measurements. Release builds (LTO) and debug builds of main 9b8347a and of this change on that commit. The rebase on 11c3671 changed no line of
production/and only type annotations inBake.ts.bloaty,perf,valgrindandstraceare not installed in the build container.WebCoreJSBuiltins.cpp: debug 5735 to 5743, release 3552 to 3560). Theobjectannotation emits 0 bytes.build_with_vm+63 bytes (nm -S, 12,959 to 13,022)..text(83,497,513),.rodata(22,574,028) and the stripped binary (109,037,072 bytes) have equal sizes.objdump:add 0x38(%r15),%r14dbecomescmpb $0x0,0x38(%r15)andje). A build of 1,000 pages at depth 3: 323,013,641 and 321,667,711 user instructions (medians of 5 interleaved runs,perf_event_opencounter). The spread between runs is 7.8 million or more, so the 4,000 instructions that the static count predicts do not show.PerThread::output_filecalls (gdb hit counts): +0 for a route with no layout (2 and 2), +1 for each layout above a route (18 to 26 for 8 pairs of a route and a layout).import()calls (1 and 1). The fixture of case 1: 7import()calls for 7 route files.dist/(diff -r). The fixtures have 1, 8 and 1,000 pages, and one has server components and a client entry point.read19,write9,openat26,close24,getdents6416 on every run of both. Totals are 555 to 608 and 549 to 590, and the spread isfutex.no_client: 2 of 4 routes change against a rename-only build (/docs,/docs/intro). Without server components 0 of 4 change.Self-review. 19 concerns.
meta.styles.on_router_collision_error. bake: failbun build --appwhen two files match the same route pattern #47146 counts every collision.bun build --appwhen two files match the same route pattern #47146 and bake: fail the build when a component throws during static prerender #33216 carry them.