What happened?
Summary
hunk crashes on startup when the directory Bun uses to extract the embedded
OpenTUI native library ($BUN_TMPDIR, else $TMPDIR, else /tmp) does not
exist or is not writable by the running user. The error message points at the
virtual $bunfs path and gives no hint about the actual cause, which made this
hard to diagnose.
Environment
- hunk version:
0.22.0
- OS: Ubuntu 26.04 (x86_64) container image, run via Docker Desktop on macOS
- Container started with a read-only root filesystem (
--read-only)
- User inside container: non-root (uid 1000),
$HOME writable at build time only
Steps to reproduce
Minimal, no container needed:
TMPDIR=/nonexistent hunk show
Realistic case:
- Install hunk in a Docker image (
curl -fsSL https://hunk.dev/install.sh | sh).
- Run the container with
--read-only and no writable /tmp (no --tmpfs).
- Inside a git repo, run
hunk show.
Actual behavior
hunk: Failed to initialize OpenTUI render library: Failed to open library "/$bunfs/root/libopentui-1qjv5tb2.so": /$bunfs/root/libopentui-1qjv5tb2.so: cannot open shared object file: No such file or directory
Expected behavior
Either hunk works with a read-only filesystem, or it fails with a message that
names the real problem (temp directory not writable / not exec-mountable) and
how to fix it.
Root cause
hunk is a bun build --compile binary. libopentui.so lives inside the
executable's embedded filesystem (/$bunfs/root/...). At runtime Bun's
dlopen writes it out to <tmpdir>/.bun-<uid>-<content-hash>.so (mode 0600)
and then dlopens the on-disk copy. If that write fails, Bun silently falls back
to calling dlopen() on the literal /$bunfs/... path, which glibc cannot
resolve, producing the misleading error above.
Confirmed with strace on a minimal Bun 1.4.2 standalone binary embedding a
.so:
- writable tmpdir:
openat("/tmp/.bun-<uid>-<hash>.so", O_RDONLY) → works
TMPDIR=/nonexistent, or a tmpdir not writable by the current uid, or a
read-only rootfs: identical "cannot open shared object file" error
- if the tmpdir is mounted
noexec the failure is different
("failed to map segment from shared object")
Once the cache file exists, Bun only stats and opens it read-only; no further
writes are needed.
Workarounds
- Mount a writable, exec-allowed tmpfs and point Bun at it:
docker run --read-only --tmpfs /var/tmp/bun:rw,exec,mode=1777 -e BUN_TMPDIR=/var/tmp/bun ...
(Docker's --tmpfs defaults to noexec, so exec must be explicit.)
- Pre-populate the cache at image build time by running hunk once as the
same uid (needs a pty and a diff, e.g. under script + timeout), so the
read-only rootfs at runtime is fine. This is what I ended up using.
Suggestions
- Detect the
$bunfs path in this error and print an actionable message:
the temp directory must exist, be writable by the current user, and not be
mounted noexec; mention BUN_TMPDIR/TMPDIR.
- Consider probing the tmpdir (
access(W_OK) or a test write) before
initializing OpenTUI so the failure is explicit rather than a fallback.
- Optionally offer a documented cache location (e.g.
$XDG_CACHE_HOME/hunk)
so users of read-only or hardened environments have a supported knob, and
document this in the install/README section on containers.
- This is arguably also a Bun issue (silent fallback instead of surfacing the
write error).
Version
0.22.0
What happened?
Summary
hunk crashes on startup when the directory Bun uses to extract the embedded
OpenTUI native library (
$BUN_TMPDIR, else$TMPDIR, else/tmp) does notexist or is not writable by the running user. The error message points at the
virtual
$bunfspath and gives no hint about the actual cause, which made thishard to diagnose.
Environment
0.22.0--read-only)$HOMEwritable at build time onlySteps to reproduce
Minimal, no container needed:
Realistic case:
curl -fsSL https://hunk.dev/install.sh | sh).--read-onlyand no writable/tmp(no--tmpfs).hunk show.Actual behavior
Expected behavior
Either hunk works with a read-only filesystem, or it fails with a message that
names the real problem (temp directory not writable / not exec-mountable) and
how to fix it.
Root cause
hunk is a
bun build --compilebinary.libopentui.solives inside theexecutable's embedded filesystem (
/$bunfs/root/...). At runtime Bun'sdlopenwrites it out to<tmpdir>/.bun-<uid>-<content-hash>.so(mode 0600)and then dlopens the on-disk copy. If that write fails, Bun silently falls back
to calling
dlopen()on the literal/$bunfs/...path, which glibc cannotresolve, producing the misleading error above.
Confirmed with
straceon a minimal Bun 1.4.2 standalone binary embedding a.so:openat("/tmp/.bun-<uid>-<hash>.so", O_RDONLY)→ worksTMPDIR=/nonexistent, or a tmpdir not writable by the current uid, or aread-only rootfs: identical "cannot open shared object file" error
noexecthe failure is different("failed to map segment from shared object")
Once the cache file exists, Bun only
stats and opens it read-only; no furtherwrites are needed.
Workarounds
docker run --read-only --tmpfs /var/tmp/bun:rw,exec,mode=1777 -e BUN_TMPDIR=/var/tmp/bun ...(Docker's
--tmpfsdefaults tonoexec, soexecmust be explicit.)same uid (needs a pty and a diff, e.g. under
script+timeout), so theread-only rootfs at runtime is fine. This is what I ended up using.
Suggestions
$bunfspath in this error and print an actionable message:the temp directory must exist, be writable by the current user, and not be
mounted
noexec; mentionBUN_TMPDIR/TMPDIR.access(W_OK)or a test write) beforeinitializing OpenTUI so the failure is explicit rather than a fallback.
$XDG_CACHE_HOME/hunk)so users of read-only or hardened environments have a supported knob, and
document this in the install/README section on containers.
write error).
Version
0.22.0