Skip to content

Bun fails inside aarch64 Linux guest on UTM SE (iOS, TCTI) #7722

@ITJesse

Description

@ITJesse

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions