Skip to content

bundler: log the resolve error for an entry point too long for a path buffer - #38391

Open
robobun wants to merge 1 commit into
mainfrom
farm/2191c16d/build-long-entrypoint-error
Open

bundler: log the resolve error for an entry point too long for a path buffer#38391
robobun wants to merge 1 commit into
mainfrom
farm/2191c16d/build-long-entrypoint-error

Conversation

@robobun

@robobun robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Problem

  • Bun.build({ entrypoints: [name] }) where cwd/name does not fit a path buffer (4096 bytes on Linux, 1024 on macOS) aborts the process instead of failing the build: panic: index out of bounds: the len is 0 but the index is 0 (release) / assertion failed: chunks.len() > 0 in generateChunksInParallel.rs (debug).
  • With a second, valid entry point, Bun.build({ entrypoints: ["./valid.js", name] }) instead succeeds: one output, no logs, the long entry point silently dropped.
  • new Worker(name) with such a path fires its error event with BuildMessage: undefined (the "Worker prints error: undefined" face from the path-length census).
  • Cause: Transpiler::resolve_entry_point (src/bundler/transpiler.rs) is documented as logging the resolve error before returning it, and all of its callers rely on that (enqueue_entry_points_normal in bundle_v2.rs just continues on Err; the Worker code turns the log into the event's message). The guard added for paths too long to build the cache-buster name in a PathBuffer returned Err from a match arm ahead of the logging, so for those paths nothing was logged: the bundle went on with zero entry points, and the Worker converted an empty log.

Fix

  • The length guard now only skips the directory-cache bust (the part that needs the buffer) and falls through to the existing logging, so an over-long entry point is reported exactly like any other missing one: ModuleNotFound resolving "<name>" (entry point). Entry points that fit take the same path as before.
  • Verified:
    • test/bundler/bun-build-api.test.ts, "an entry point too long for a path buffer is reported like any other missing one": a child bun builds a 100000-byte entry point (longer than the buffer on Windows too) alone and next to a valid one; both must fail with that one message and no outputs.
    • test/js/web/workers/worker.test.ts, "names the entry point when its path is too long for a path buffer": the error event's message must name the entry point.
    • USE_SYSTEM_BUN=1: the build test fails (child aborts with the panic above; the two-entry-point case on its own reports success: true, 1 output), the worker test fails with Received: "BuildMessage: undefined". bun bd test on this branch: both pass; the full bun-build-api.test.ts and plugins.test.ts files pass too. Three unrelated terminate() tests in worker.test.ts fail identically on an unmodified main debug build in this container (release passes them).
  • The narrow window where cwd/name is within a few bytes of the buffer still reaches the unchecked load_as_file write in the resolver; that is resolver: bound load_as_file path before writing into its PathBuffer #35857's site and is not touched here.

Background

  • Bun.build resolves each entry point up front with resolve_entry_point; on failure it retries once after invalidating the resolver's cache of the entry point's directory (so a file created moments ago is found), then logs the error. The bundle runs to the linker regardless and fails at the end if the log has errors, which is why a failure that was not logged turns into a build with no entry points at all.
  • The cache-buster name is built in a stack PathBuffer ([u8; MAX_PATH_BYTES]) with unchecked joins, hence the guard: a path that long cannot name a directory that exists, so there is nothing to invalidate.
  • A Worker's entry point is resolved by the same function; the worker turns whatever the resolver logged into the error event's message.

no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/bundler/bun-build-api.test.ts

… buffer

resolve_entry_point's callers rely on it logging the error before returning
it. The arm that skips the directory-cache bust for an entry point whose
path cannot fit a PathBuffer returned without logging, so the entry point
was silently dropped: Bun.build succeeded without it when another entry
point was given and hit the linker's no-chunks assertion when it was the
only one, and a Worker given such a path reported "BuildMessage: undefined".
Skip only the cache bust for those paths and fall through to the logging.
@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3eef56c6-8184-4a2f-adae-d33222092e6d

📥 Commits

Reviewing files that changed from the base of the PR and between a0921e1 and f78f612.

📒 Files selected for processing (3)
  • src/bundler/transpiler.rs
  • test/bundler/bun-build-api.test.ts
  • test/js/web/workers/worker.test.ts

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

@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.

LGTM — the length guard now falls through to the existing error-logging path instead of returning ahead of it, so an over-long entry point is reported like any other unresolved one.

What was reviewed:

  • resolve_entry_point control flow: break 'name false skips only the cache-bust/retry (which needs the PathBuffer), then reaches log_mut().add_error_fmt(...); entry points that fit take the identical path as before.
  • Confirmed callers (enqueue_entry_points_normal, the Worker resolve path) rely on the log entry rather than the Err — the doc comment on this function says as much, and the fix is at the right layer.
  • Both new tests spawn a child / await the event, assert exact messages, cover the alone + alongside-valid-entry cases and the Worker face, and follow harness conventions (Buffer.alloc, tempDir, concurrent pipe drain, exit-code last).
Extended reasoning...

Overview

The PR relocates an existing length guard in Transpiler::resolve_entry_point (src/bundler/transpiler.rs). Previously the guard was a separate Err(err) if <too long> => Err(err) match arm ahead of the arm that busts the directory cache, retries, and — crucially — logs the error. That arm returned Err without logging, so callers that consume the log (enqueue_entry_points_normal just continues on Err; the Worker turns the log into the error event's message) saw nothing: a build with zero entry points panicked in the linker, a build with a valid sibling silently succeeded, and a Worker fired BuildMessage: undefined.

The fix moves the same predicate inside the 'name labelled block as break 'name false, so the cache-bust step (the only part that needs the fixed-size PathBuffer) is skipped and control falls through to self.log_mut().add_error_fmt(...). The error is now logged with the same format as every other unresolved entry point. No behavior change for entry points that fit the buffer.

Two tests are added: one in bun-build-api.test.ts that spawns a child bun and asserts a 100 000-byte entry point produces exactly one ModuleNotFound BuildMessage and zero outputs, both alone and alongside a valid entry point; and one in worker.test.ts that asserts new Worker(longSpecifier) fires an error event whose message names the specifier. Both follow the repo's harness conventions (tempDir, bunEnv, Buffer.alloc(n, fill), concurrent stdout/stderr/exit drain, exit code asserted last, child process so a crash surfaces as a failed assertion).

Security risks

None. This is error-message plumbing on an already-failing resolve path; no new parsing, no allocation sized by the input, no privilege or filesystem effects. The over-long path never reaches join_abs_string_buf_z (unchanged from before — the guard predicate is the same), so the PathBuffer unchecked-write concern is unaffected.

Level of scrutiny

Low. The Rust change is an 8-line relocation of an existing condition within one function, changing which arm falls through to already-existing logging code. The function's own doc comment states it reports the error to the log; the fix makes the too-long branch honor that contract. The tests are additive and placed in the existing files for their modules.

Other factors

The PR description documents USE_SYSTEM_BUN=1 failing both new tests with the exact symptoms described (panic / success: true / BuildMessage: undefined) and bun bd test passing, plus that the full bun-build-api.test.ts and plugins.test.ts still pass. It explicitly scopes out the adjacent within-a-few-bytes resolver write (#35857). No prior human reviews or outstanding comments on the timeline (only a CodeRabbit rate-limit notice).

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Status: nothing outstanding from review; the change is the 8-line relocation of the length guard described above plus the two tests, both verified failing on the released build and passing here. Sibling PRs from the same census: #38379 (shell mkdir/touch) and #38392 (_nodeModulePaths, onResolve paths); all three are independent. Waiting on CI.

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 4:05 AM PT - Aug 14th, 2026

@robobun, your commit f78f612 has some failures in Build #95583 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 38391

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

bun-38391 --bun

@robobun

robobun commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

Ready for a maintainer. The change is unchanged since it was opened (head f78f612): the length guard in resolve_entry_point now only skips the cache bust, so the error is logged like any other; the Bun.build test and the Worker test both fail on the released build and pass here, and both passed on every lane of build 95583. The lanes that build reports as failed are retry-passed flakes in unrelated files (fs read-stream pos, inspect-error-leak, bun-patch and filter-workspace on Windows aarch64, a napi threadsafe-function batch, cluster-shared-leak, malformed-integrity-base64), so I am not pushing a retrigger for them. Sibling PRs from the same census: #38379, #38392.

robobun added a commit that referenced this pull request Aug 15, 2026
…-no-bundle entries

resolve_entry_point is back to what main has: #38391 already carries the
same change with tests that also cover Windows and Bun.build. The browser
field message gets the same "(entry point)" suffix #38778 uses, and the
new test pins that --no-bundle now resolves "entry" to entry.ts the way a
bundling build does.
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