Bun fails inside aarch64 Linux guest on UTM SE (iOS, TCTI)
- Dominant language
- Swift
- Stars
- 35.5k
- Forks
- 1.8k
- Avg merge
- 5d 5h
- Merged PRs (30d)
- 7
Description
# 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
```sh
# 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
Contributor guide
Assessment
This issue has not been assessed yet.