Bun fails inside aarch64 Linux guest on UTM SE (iOS, TCTI)
Summary
Running Bun 1.3.14 inside an Alpine aarch64 musl guest on UTM SE (iOS, QEMU built with --enable-tcg-threaded-interpreter), several Bun code paths fail:
bun /path/to/script.js reports Module not found even when the file exists
Bun.file(arg) throws ERR_INVALID_ARG_VALUE for string, Uint8Array, and URL arguments
fs.readFileSync with a string path produces no output and exits 0
bun -e, bun -p, and bun - (stdin) all work. JS-level typeof, instanceof, string iteration, and TextEncoder all produce correct results.
Environment
- Affected: UTM SE on iOS, on iPad Pro (M5) (aarch64 TCTI)
- Unaffected: UTM (with JIT) using the same Bun binary and same guest image
- Guest: Alpine Linux aarch64 musl, kernel 6.18.32-0-virt, 4 KiB page size
- Bun 1.3.14+0d9b296af, official
linux-aarch64-musl build
Minimal reproduction
# In guest:
apk add curl bash strace
wget -O /tmp/bun.zip https://github.com/oven-sh/bun/releases/latest/download/bun-linux-aarch64-musl.zip
unzip -o /tmp/bun.zip -d /tmp/
alias bun=/tmp/bun-linux-aarch64-musl/bun
echo 'console.log("hi")' > /tmp/t.js
# 1. CLI module loader reports not-found although the file is present
bun /tmp/t.js
# error: Module not found '/tmp/t.js'
# Compare: a truly nonexistent path is reported with double quotes
bun /nope.js
# error: Module not found "/nope.js"
# 2. Bun.file rejects every accepted argument type
bun -e 'try{Bun.file("/tmp/t.js")}catch(e){console.log(e.code, e.message)}'
# ERR_INVALID_ARG_VALUE The argument 'path' must be a string, Uint8Array, or URL without null bytes. Received "/tmp/t.js"
bun -e 'const u=new TextEncoder().encode("/tmp/t.js"); try{Bun.file(u)}catch(e){console.log(e.code,e.message)}'
# Same error
bun -e 'try{Bun.file(new URL("file:///tmp/t.js"))}catch(e){console.log(e.code,e.message)}'
# Same error
# 3. JS-level checks on the same string return correct values:
bun -e 'const s="/tmp/t.js"; console.log(typeof s, s.length, s.includes("\0"))'
# string 9 false
bun -e 'console.log([] instanceof Array, {} instanceof Object)'
# true true
# 4. Paths that bypass the file-path validator work:
bun -e 'console.log("hi")' # hi
echo 'console.log("hi")' | bun - # hi
bun -p '1+1' # 2
# 5. fs.readFileSync with a string path silently exits 0 with no output
bun -e 'console.log(require("fs").readFileSync("/tmp/t.js","utf8"))'
# (no stdout, no stderr, exit 0)
strace evidence
bun /tmp/t.js opens and stats the file successfully, then closes it without reading, and prints the not-found error:
openat(AT_FDCWD, "/tmp/t.js", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=18, ...}) = 0
close(3) = 0
...
write(2, "error: Module not found '/tmp/t.js'", ...)
The kernel returned the correct file info; the bail happens after.
python3 -c "print(open('/tmp/t.js').read())" on the same file in the same guest prints the file contents normally, so the filesystem and syscalls themselves are not the issue.
Eliminated hypotheses
- Not a page-size issue — guest is 4 KiB
- Not a corrupted Bun binary —
bun -e, bun -p, stdin all work
- Not path-string corruption — strace shows the correct path on
openat; the validator's error message echoes the input back exactly
- Not null bytes in the path —
String.prototype.includes('\0') returns false; byte iteration confirms no null bytes; the same failure occurs for paths of length 4, 5, 9, and 18 characters
- Not JS-level
typeof/instanceof — those return correct results
Bun fails inside aarch64 Linux guest on UTM SE (iOS, TCTI)
Summary
Running Bun 1.3.14 inside an Alpine aarch64 musl guest on UTM SE (iOS, QEMU built with
--enable-tcg-threaded-interpreter), several Bun code paths fail:bun /path/to/script.jsreportsModule not foundeven when the file existsBun.file(arg)throwsERR_INVALID_ARG_VALUEfor string, Uint8Array, and URL argumentsfs.readFileSyncwith a string path produces no output and exits 0bun -e,bun -p, andbun -(stdin) all work. JS-leveltypeof,instanceof, string iteration, andTextEncoderall produce correct results.Environment
linux-aarch64-muslbuildMinimal reproduction
strace evidence
bun /tmp/t.jsopens and stats the file successfully, then closes it without reading, and prints the not-found error:The kernel returned the correct file info; the bail happens after.
python3 -c "print(open('/tmp/t.js').read())"on the same file in the same guest prints the file contents normally, so the filesystem and syscalls themselves are not the issue.Eliminated hypotheses
bun -e,bun -p, stdin all workopenat; the validator's error message echoes the input back exactlyString.prototype.includes('\0')returnsfalse; byte iteration confirms no null bytes; the same failure occurs for paths of length 4, 5, 9, and 18 characterstypeof/instanceof— those return correct results