Conversation
`bun ./x.css`, `bun run ./x.css` and an absolute path take the fast path `maybe_open_with_bun_js`, which booted every existing file. A file with a css, text, toml, yaml or json loader was imported as a module. Nothing ran, and the exit code was 0. `bun x.css` goes through the resolver and reports `Cannot run`. The fast path now applies the same loader check as the resolve path, with the same loader lookup (`--loader`, bunfig `[loader]`, then the default), and reports the file through the same failure tail.
|
Warning Review paused — included plan limit reachedKeep your review moving with free on-demand reviews.
On-demand reviews are free for one more day.
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Promotion and pricing detailsOn-demand reviews are free for one more day. After that, they cost $0.25 per reviewed file. Review limit detailsOr wait 4 minutes for your next included review. Limit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configurationConfiguration used: Repository: oven-sh/bun/.coderabbit.yaml Review profile: ASSERTIVE Plan: Essentials Run ID: 📒 Files selected for processing (3)
Comment |
|
Updated 3:52 PM PT - Sep 19th, 2026
✅ @robobun, your commit 98bf8f39631221f68175ff7ed9343e284bab4d5a passed in 🧪 To try this PR locally: bunx bun-pr 43480That installs a local version of the PR into your bun-43480 --bun |
|
Status Reproduction (Linux x64, Bun 1.4.3-canary.1+367d939d9): mkdir -p /tmp/norun && cd /tmp/norun
echo 'body{color:red}' > x.css
bun x.css # error: Cannot run "/tmp/norun/x.css", exit 1
bun ./x.css # prints nothing, exit 0With this PR, Test proof: USE_SYSTEM_BUN=1 bun test test/cli/install/bun-run.test.ts -t "cannot run|no_run_json" # 26 fail, 16 pass
bun bd test test/cli/install/bun-run.test.ts -t "cannot run|no_run_json" # 42 pass |
There was a problem hiding this comment.
Beyond the inline findings, I also checked the log_errors == false branch of the new nothing_ran helper, which returns false and lets maybe_open_with_bun_js fall through to script/binary lookup for an unrunnable file — it is unreachable today because src/runtime/cli/mod.rs:1466 only sets log_errors: false together with --if-present, and nothing_ran returns true for --if-present first.
Extended reasoning...
Findings are already posted inline (loader-override cases such as sh and md remaps, the --loader flag precedence, and the resolve-path which() fall-through), and two further verified findings are not posted, so approval is off the table. The one extra thing I traced myself was whether nothing_ran returning false when log_errors is false could let the fast path fall through and execute an executable data file via which(); every caller except mod.rs:1466 passes log_errors: true, and that site only passes false when --if-present is set, which nothing_ran short-circuits to true before reaching the log_errors check. So that path is currently dead rather than a live bug, though it is fragile if a future caller passes false for another reason.
Findings marked 🟡 are optional suggestions and need no follow-up push.
Additional findings (outside the current diff — GitHub can't attach inline comments there):
-
🟣
src/runtime/cli/run_command.rs— pre-existing: a user runningbun run sub/tool.cssstill has the data file executed as a binary (or gets "Failed to run"), whilebun run ./sub/tool.cssnow gets "Cannot run". The resolve step records the unrunnable file at run_command.rs:2587 but then still callswhich()at run_command.rs:2663. On POSIXwhich()returns any target containing/that has the executable bit; on Windowswhich_winreturns any target spelled with a directory and an extension. Fix: when the resolve step found an existing file Bun cannot run and the target contains a path separator, go straight tonothing_ranand skip the binary lookup, keeping the PATH search for bare names. The new test only covers the./tool.cssspelling. [also at: src/runtime/cli/run_command.rs:2588 - pre-existing: a user runningbun run sub/tool.css(a relative path with a directory but no leading./) still has the data file executed as a shell binary when it has the executable bit, whilebun ./tool.cssnow correctly reportsCannot run.]Extended reasoning...
The PR description itself cites
chmod +x sub/tool.css && bun sub/tool.cssexecuting the file as the reason the fast path must not fall through, but leaves that spelling on the resolve path unchanged. Trigger: a target with a directory component that does not start with.and is not absolute, e.g.bun run sub/tool.css,bun run src/config.json, on a file with the executable bit (common for whole trees on WSL /mnt/c, FAT/NTFS or some bind mounts, where every file reports X_OK). exec_with_cfg: try_fast_run stays false at run_command.rs:2340-2349 because the name starts withsand.css/.jsonhas no runnable default loader. Script lookup finds nothing. Resolver at run_command.rs:2547 fails forsub/tool.cssas a package path, the./retry at run_command.rs:2555 resolves it. entry_point_loader returns Css, can_run_entry_point is false, so…Verification: pre-existing. acknowledged in diff: the PR description's "Why not fall through to the resolve path" section states
chmod +x sub/tool.css && bun sub/tool.cssexecutes the file today and thatwhich_winaccepts a dir+extension spelling on Windows; that claim is accurate, but the PR only guards the fast path and leaves this route intact. Trigger: a target with a directory component that does…
- A `.sh` entry point runs in the Bun shell whatever its loader is, as `boot` decides. `".sh" = "sh"` in bunfig and `--loader .sh:sh` arrive as the `file` loader, so the loader check refused the script. - The fast path passes the loader it checked to `boot_and_handle_error`, as the resolve path does. A file mapped to `md` renders and a `.md` file mapped to `ts` runs, for every spelling. - `entry_point_loader` reads the extension the way the VM's `loader_for_path` does: also as a loader name, in any case. `X.CSS` and `data.sqlite` report `Cannot run`. - For a target with a directory, the binary lookup does not execute a file that Bun cannot run. `bun sub/tool.css` executed the file if it had the executable bit. On Windows it failed with EFTYPE from uv_spawn.
- With a preload, the fast path boots an explicit path to a data file as before. A plugin from the preload can turn the file into code, and only the VM knows that. `test/cli/run/preload-test.test.js` documents this for a `.txt` entry point. That test passes, so it is no longer a todo. - Every comment that this branch adds in src/ is one line.
Cannot run for an explicit path to a data file (bun ./x.css)Cannot run for a path to a data file, for every spelling (bun ./x.css)
|
Reply to the review findings that have no open thread:
|
Problem
bun ./x.cssandbun /abs/x.cssprint nothing and exit 0.bun x.cssprintserror: Cannot run "/tmp/norun/x.css"andnote: Bun cannot run css files directly, then exits 1. Every data loader does the same.maybe_open_with_bun_js(src/runtime/cli/run_command.rs), the fast path for a target that starts with.or is absolute. It boots every existing file. The resolve block ofexec_with_cfgchecks the loader first. Fix bun run folder #15117 (Bun 1.2.0) dropped that check here.bun sub/tool.cssexecutes the file as a binary if it has the executable bit. On Windows it fails withEFTYPEfromuv_spawn.Fix
entry_point_loader(the lookup order of the VM'sloader_for_path) andcan_run_entry_point. The fast path reports through the shared failure tailnothing_ranand passes its loader on.test/cli/run/preload-test.test.jscovers that and is no longer a todo.test/cli/install/bun-run.test.tson Linux and Windows x64. 26 new tests fail on the released binary.Background
RunCommand::exec_with_cfghandlesbun <target>andbun run <target>. It tries the fast path, apackage.jsonscript, module resolution, then a binary.can_be_run_by_bun()covers JS, TS, JSX, TSX, wasm and shell. HTML starts the dev server and Markdown renders. Every other loader only exports data.--preload, bunfigpreload) runs before the entry point. It can register a plugin whoseonLoadreplaces the contents of a file.Notes
Before and after (Linux x64, files in
/tmp/norun, output and exit code, no preload)bun x.cssFile not found, 1File not found, 1Cannot run, 1Cannot run, 1bun ./x.cssFile not found, 1Cannot run, 1bun x.jsonModule not found, 1Cannot run, 1Cannot run, 1bun ./x.jsonCannot run, 1bun sub/tool.css(executable bit)Cannot run, 1bun sub\style.css(Windows)EFTYPE ... (uv_spawn()), 1Cannot run, 1History
canBeRunByBun(), for every spelling.maybeOpenWithBunJSfor./and absolute paths, with no loader check. From then on every data file "ran" and exited 0.canBeRunByBun()and put the check on the resolve path. Its test expectsbun ./no_run_jsonto exit 1.bun ./no_run_json.jsontakes the fast path, so it kept exit 0.Cannot runmessage, also on the resolve path only.What the review of the first version changed
The first version refused every data file on the fast path. The review found these cases, and each now has a test:
bun --preload ./plugin.ts ./entry.yamlruns the plugin's code on the base, andtest/cli/run/preload-test.test.js("as entry point") documents that for a.txtfile. The first version refused it. Now the fast path refuses only when there is no preload.".sh" = "sh"in bunfig, or--loader .sh:sh. Both store theshloader as thefileloader, so the check refusedbun ./script.sh.bootruns every.shpath in the Bun shell, socan_run_entry_pointaccepts a.shpath first. This also repairsbun run script.shwith that mapping, which the base refuses withBun cannot run file files directly.md. The fast path checked one loader and booted with none. With".txt" = "md",bun ./notes.txtprinted nothing andbun notes.txtrendered. With".md" = "ts",bun ./x.mdrendered the source andbun run x.mdran it.boot_and_handle_errornow takes the loader from its caller, and its fallback to the default loader is gone.X.CSS,data.sqlite,note.file. The VM also reads an extension in upper case, and a loader name as an extension. The CLI did not, so both spellings printed nothing.entry_point_loadernow ends withLoader::from_string, asloader_for_pathdoes.README.MDnow renders.bun sub/tool.css. For a target with a directory,which()does not search$PATH. It looks at that path only. The lookup now skips a file thatcan_run_entry_pointrefuses. A bare name still goes tonode_modules/.bin(bun nxnext tonx.json), and on Windowsbun run sub/toolstill runssub\tool.cmdnext tosub\tool.json.Known gaps that this PR does not close
bun ./x.cssstill prints nothing and exits 0. Only the VM knows whether a plugin takes the entry point, so a report for that case needs a check in the module loader.bun run --loader .txt:ts ./code.txtin a project whose bunfig has a[loader]table reportsBun cannot run text files directly. On the base it prints nothing. In both cases the cause is that the late bunfig load replaces the--loaderflags, so the VM gets the text loader too. bunfig: keep CLI flags ahead of bunfig.toml when it is loaded after argv (bun run) #38599 fixes that load order.nodeshim (exec_as_if_node) still prints nothing and exits 0 fornode ./x.css. Node runs.jsonand.nodeentry points and parses every other extension as JavaScript, so theCannot runrule does not fit there as it is. Make the entry point of thenodeshim the main module #43409 also moves the resolve of the shim's entry point, and a loader check belongs after that resolve.bun ./X.HTMLnow reaches the HTML entry point code, which accepts only a.htmlname and throwsNo HTML files found matching. An extension that bunfig maps tohtmldoes the same on the base. Before,X.HTMLprinted nothing.What else changes
bun words.ts(no./) also takes the fast path, because.tsruns by default. With".ts" = "text"in bunfig it printed nothing and exited 0, andbun run words.tsreportedCannot run. Now both report it.bun ./addon.nodereportsBun cannot run napi files directly, asbun addon.nodealready does. Before, it loaded the addon and exited 0.bun --watch ./x.cssandbun --hot ./x.cssreport the file and exit 1, asbun --watch x.cssalready does.What does not change
bun ./page.html,bun ./README.md,bun ./mod.wasm,bun ./script.sh, a file without an extension, and a file with an unknown extension.bun ./dir), a missing file, and--if-present.bun --if-present ./x.cssexits 0 with no output before and after, likebun --if-present x.css.entry_point_loaderreadsctx.args.loaders, whichconfigure_env_for_runclones into the transpiler whose map the old code read. Both take the last entry for an extension.Why the fast path does not fall through to the resolve path
The resolve path would find the same file and print the same message. But the fast path is for explicit paths, which the docs call source files, and the lookups after the resolve step are for names.
nothing_rangives the same message, exit code and--if-presentbehavior without them.Tests
'bun run' prioritytable:./no_run_json.jsonand the absolute path, each withbun,bun run,bun --bunandbun --bun run. All 8 fail on the released binary.x,./x, absolute,../x, and.\xon Windows) with and withoutrun, the loader name in the message for six targets,--if-present, a bunfig[loader]entry in both directions,--loaderin both directions and with a repeated extension, theshmapping, themdmappings, a preload plugin (flag and bunfig), a symlink in both directions, an executable file with and without a directory, and the Windows.cmdcase. 18 of 22 fail on the released binary on Linux. The 4 that pass pin behavior that must not change: the spelling without./,--if-present, and the preload plugin.test/cli/run/preload-test.test.js: "as entry point > works from CLI" passes on the base and with this PR, on Linux and Windows, so it is a test again. The first version of this PR failed it.test/cli/install/bun-run.test.ts326 pass. Windows x64 debug build: 447 pass.test/regression/issue/1365.test.ts,test/cli/run/run_command.test.ts,if-present.test.ts,markdown-entrypoint.test.ts,run-extensionless.test.ts,run-shell.test.ts,preload-test.test.js,test/cli/install/bun-run-bunfig.test.ts,test/js/bun/http/bun-serve-html-entry.test.ts, and the Rust source lints.no test proof · iteration 2 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/install/bun-run.test.ts