From f78f612efd798e5669f814c257d249cc707e0a53 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Fri, 14 Aug 2026 05:53:08 +0000 Subject: [PATCH] bundler: log the resolve error for an entry point too long for a path 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. --- src/bundler/transpiler.rs | 16 ++++++------- test/bundler/bun-build-api.test.ts | 36 ++++++++++++++++++++++++++++++ test/js/web/workers/worker.test.ts | 9 ++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/bundler/transpiler.rs b/src/bundler/transpiler.rs index 536e3ea26598..f037904ed55b 100644 --- a/src/bundler/transpiler.rs +++ b/src/bundler/transpiler.rs @@ -459,14 +459,6 @@ impl<'a> Transpiler<'a> { pub fn resolve_entry_point(&mut self, entry_point: &[u8]) -> crate::Result { match self._resolve_entry_point(entry_point) { Ok(r) => Ok(r), - // Nothing that long names a directory whose cache could be stale - // (and the join below has a PathBuffer to fit `top_level_dir/entry/..` in). - Err(err) - if self.fs().top_level_dir.len() + entry_point.len() + 4 - > bun_paths::MAX_PATH_BYTES => - { - Err(err) - } Err(err) => { let mut cache_bust_buf = bun_paths::PathBuffer::uninit(); @@ -477,6 +469,14 @@ impl<'a> Transpiler<'a> { // disjoint mutable borrows of `cache_bust_buf` across `break`, // so compute `busted` directly instead. let busted: bool = 'name: { + // Nothing that long names a directory whose cache could be + // stale (and neither buster name below would fit + // `cache_bust_buf`). + if self.fs().top_level_dir.len() + entry_point.len() + 4 + > bun_paths::MAX_PATH_BYTES + { + break 'name false; + } if bun_paths::is_absolute(entry_point) { let dir = bun_paths::resolve_path::dirname::( entry_point, diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index 19d6444da1b4..ecdaa4953f44 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -214,6 +214,42 @@ describe("Bun.build", () => { } }); + test("an entry point too long for a path buffer is reported like any other missing one", async () => { + // Resolving it failed without logging anything, so the build went on + // with the entry point silently dropped: a successful build when another + // entry point was given, a crash in the linker when it was the only one. + // Runs in a child so the crash shows up as a failed assertion. + using dir = tempDir("build-api-long-entrypoint", { "valid.js": "console.log(1);" }); + const fixture = /* ts */ ` + // Longer than the path buffer on every platform, Windows included. + const long = Buffer.alloc(100_000, "a").toString(); + const report = async (entrypoints: string[]) => { + const { success, outputs, logs } = await Bun.build({ entrypoints, throw: false }); + return { success, outputs: outputs.length, logs: logs.map(log => [log.name, log.message]) }; + }; + console.log(JSON.stringify({ + alone: await report([long]), + withValidEntryPoint: await report(["./valid.js", long]), + })); + `; + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", fixture], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + const notFound = { + success: false, + outputs: 0, + logs: [["BuildMessage", `ModuleNotFound resolving "${Buffer.alloc(100_000, "a").toString()}" (entry point)`]], + }; + expect(JSON.parse(stdout)).toEqual({ alone: notFound, withValidEntryPoint: notFound }); + expect(exitCode).toBe(0); + }); + test("returns output files", async () => { Bun.gc(true); const build = await Bun.build({ diff --git a/test/js/web/workers/worker.test.ts b/test/js/web/workers/worker.test.ts index 92d1011a59b0..31a502be4814 100644 --- a/test/js/web/workers/worker.test.ts +++ b/test/js/web/workers/worker.test.ts @@ -369,6 +369,15 @@ describe("web worker", () => { expect(err.message).toBe("5"); expect(err.error).toBe(null); }); + + test("names the entry point when its path is too long for a path buffer", async () => { + // Resolving it failed without logging anything, so the event carried + // "BuildMessage: undefined". Longer than the buffer on every platform. + const specifier = "./" + Buffer.alloc(100_000, "w").toString(); + const worker = new Worker(specifier); + const [err] = await once(worker, "error"); + expect(err.message).toBe(`BuildMessage: ModuleNotFound resolving "${specifier}" (entry point)`); + }); }); describe("terminate() races and lifecycle edges", () => {