[BUG] freebsd linuxator
Status Open
Reported on v2.1.212
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 5 comments · opened Aug 31, 2026
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When I upgrade claude version to latest claude running in linuxator stopped working because could create temporary files.
What Should Happen?
It should work properly. bash subprocesses should work without issue.
Error Messages/Logs
Claude Code here is the Linux build under linuxulator. The jail's `uname` is FreeBSD,
so claude.ai/install.sh won't fetch a Linux build. Instead MOUNT the host's
install (/usr/local/libexec/claude-code) ro and symlink it onto PATH -- it
runs via linuxulator using the /compat/linux libs. No install step at all.
CRITICAL: set DISABLE_AUTOUPDATER=1 (in ~claude/.profile, done by the overlay).
Otherwise Claude's built-in updater silently downloads newer builds into
~/.local/share/claude/versions and runs those, shadowing the mount. Versions
from ~2.1.25x switched from the /dev/fd idiom (works here via the linrdlnk
fdescfs, #2) to /proc/self/fd, whose fd symlinks resolve EMPTY inside the
jail's linprocfs -- so mkdir("/proc/self/fd/N/child") returns ENOTDIR and every
Bash/Write tool call dies at scratchpad creation. (ktrace pinpointed it; the
host is unaffected because /proc/self/fd works outside a jail.) The updater
pins to whatever the host claude-code PKG is; if that pkg later moves past the
/dev/fd versions, mount a specific pinned build instead. First thing to check
if the jail's Bash tool breaks: `claude --version` in the jail vs the mount.
Steps to Reproduce
Update version to latest and run claude . During any task on linuxator I see that could ran bash because couldn't create temporary file.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.212
Claude Code Version
2.1.251
Platform
Anthropic API
Operating System
Other
Terminal/Shell
Other
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Independent confirmation, with a tighter bisect and one correction to the scope.
Same failure, but not in a jail
FreeBSD 14.3-RELEASE amd64,
security.jail.jailed=0,jlsempty — a plain host running the Linux build via the VS Code extension under the linuxulator (linprocfson/compat/linux/proc,fdescfson/dev/fdand/compat/linux/dev/fd,/rootnullfs-mounted at/compat/linux/root). Every Write/Edit fails withENOTDIRregardless of target path, and the Bash tool never launches a shell. Reads still work, so the session looks healthy until you try to write or run something.Worth flagging because the report above says "the host is unaffected because /proc/self/fd works outside a jail" — that isn't true for the traversal this depends on, so please don't scope the fix to jails.
The mechanism is also slightly different from the description:
ls /compat/linux/proc/self/fdlists0 1 2 3 4 …correctly, so the fd symlinks are not empty. What fails is walking through one. fdescfs implements noVOP_LOOKUPbeneath its numeric entries, so/proc/self/fd/<N>/<child>returns a genuine in-kernel ENOTDIR:(
kdumprenders Linux-ABI errnos negative, so grepping traces forerrno 20finds nothing.)Bisect: 2.1.250 good → 2.1.251 bad
Each version installed on its own, extension host restarted, and the running binary confirmed each time with
ps -axo args | grep native-binary/claude(registered ≠ running):| Version | Result |
|---|---|
| 2.1.240 | works |
| 2.1.245 | works |
| 2.1.248 | works |
| 2.1.250 | works |
| 2.1.251 | broken |
| 2.1.260 | broken |
No 2.1.249 was published, so the regression is exactly 250 → 251. Reverting to 2.1.250 restores full function, so it is the build and not environment drift. Note it is still broken in 2.1.260, nine releases after this issue was filed. ("Last working 2.1.212" in the report is much looser than necessary.)
Three symptom strings, one cause:
ENOTDIR: not a directory, mkdir '/compat/linux/tmp/claude-0/<slug>'— Bash scratch dirtask output swap refused (open refused a swapped leaf (ENOTDIR)): …/tasks/<id>.outputENOTDIR: not a directory, open '<any path>'— Write/Edit, including paths under the workspaceFailure is near-total but not perfectly deterministic — an occasional lone
echosucceeded between failures, so a single good call does not clear a version.Which change likely did it
Grepping the shipped binaries:
| String | 2.1.248 | 2.1.250 | 2.1.251 | 2.1.260 |
|---|---|---|---|---|
|
/proc/self/fd| — | 24 | 39 | — ||
swapped leaf| 0 | 0 | 2 | 2 ||
/dev/fd/| 3 | 3 | 5 | 5 |swapped leafis absent from every working build and present from 2.1.251 on, and/proc/self/fdoccurrences jump 24 → 39 across the same boundary. That points at the 2.1.251 symlink-swap/TOCTOU hardening — "Read/Write/Edit could follow a symlink swapped inside the working directory after the permission check" and "Bash output files are now created/read so a sandboxed command cannot redirect or replace them."The intent is right; re-opening a validated path by descriptor is the correct defence. The portability problem is doing it via a
/proc/self/fd/<dirfd>/<name>path string. Anopenat(dirfd, name, …)/O_PATH-relative form gets the same TOCTOU guarantee and works on FreeBSD, as does the/dev/fdidiom the older builds used.Ruled out, to save anyone repeating it
-o linrdlnkon/compat/linux/dev/fd— affectsreadlink(2)only, not traversal, so it rescues the/dev/fdidiom but not this one.stat/statxstruct mistranslation; open-flag mistranslation;/compat/linuxpath double-prefixing.stat/S_ISDIR,newfstatat,O_DIRECTORY, the create path, and glibc Linux binaries (sh,touch,mkdir,test -d) all behave correctly.Workaround
Pin 2.1.250 —
"pinned": trueinextensions.json, plus per-extension "Ignore Updates".We fixed this at the beginning of the month and hopefully should have it MFCed soon
https://github.com/freebsd/freebsd-src/commit/b3f7a438bb233c85290dcdc5241d86c49100b6b7
https://reviews.freebsd.org/D59393
@DominoTree - Any idea which releases will get the FreeBSD patch? I'm on 14.4 and hope I won't need an OS upgrade to use Claude.
For the Anthropic devs: going back to 2.1.250 means losing access to Opus 5.5 and Sonnet 5.5, so it's not a "great" workaround. We'd all appreciate getting that 1 small change reverted for a while.
@chriswells0 We should be able to merge this (and a couple subsequent related fixes) into 15 but I'll need to dig around a bit to see what 14 would look like
Surprised to see so many folks lately still on 14.x although I suppose our users aren't usually known for wanting the "latest and greatest thing" 😁️