Temp-filesystem preflight false-positives ENOSPC on filesystems with >17.6 TB free (statfs 32-bit truncation)
Bug report: temp-filesystem preflight false-positives "ENOSPC" on large (>17.6 TB) filesystems
Product: Claude Code CLI
Affected versions: 2.1.153 → 2.1.158 (latest at time of writing). Not present in ≤ 2.1.152.
Severity: Tool execution aborts ("Command output was lost") on filesystems that are nowhere near full.
Summary
Since 2.1.153, Claude Code runs a free-space preflight before capturing a
child process's stdout/stderr to its temp filesystem. On a filesystem with
more than ~17.6 TB of free space, the check computes a negative free-MB
value (due to a 32-bit truncation of f_bavail in the JS runtime's fs.statfs
binding) and aborts with:
Command output was lost: the temp filesystem at <dir> is full (-4469490MB free).
The child process's stdout/stderr writes failed with ENOSPC.
Free up space or set CLAUDE_CODE_TMPDIR to a directory on a filesystem with room.
The filesystem is not full — the write would have succeeded. Versions ≤
2.1.152 (which had no preflight) ran on the exact same machine for weeks with
no issue.
Root cause
The preflight (decompiled from 2.1.158) is:
const q = await statfs(dir);
const K = Math.floor(q.bavail * q.bsize / 1048576); // free MB
if (K < 10) return `…is full (${K}MB free)… ENOSPC…`;
if (q.files > 0 && q.ffree < 1000) return `…out of inodes…`;
q.bavail comes from the runtime's fs.statfs, which truncates the kernel's
64-bit f_bavail to a signed 32-bit int. Measured on the affected host
(a containerd overlay rootfs, 112 TB / 45.8 TB free):
| field | kernel (os.statvfs, correct) | runtime (fs.statfs, what CC uses) |
|---|---|---|
| f_bavail | 11,740,712,350 | −1,144,189,538 |
| bavail × bsize | 48,089,957,785,600 (≈ 45.8 TB) | −4,686,600,347,648 |
| → free MB (K) | 45,862,157 | −4,469,491 |
The truncation is exact:
11,740,712,350 mod 2^32 = 3,150,777,758
3,150,777,758 > 2^31 → as int32 = −1,144,189,538 ✓
So any filesystem with > 2^32 free 4 KB-blocks (≈ 17.6 TB free) tripsK < 10 and aborts. Large overlay/NFS/scratch filesystems are common on
build boxes, GPU nodes, and CI runners.
Reproduction
On a host with a >17.6 TB-free filesystem (or any fs whose fs.statfs().bavail
wraps negative — verify with the table below):
# kernel says plenty free:
python3 -c "import os;s=os.statvfs('/tmp');print(s.f_bavail*s.f_bsize//1048576,'MB')"
# runtime truncates to negative:
node -e "const s=require('fs').statfsSync('/tmp');console.log(Math.floor(s.bavail*s.bsize/1048576),'MB')"
# (bun reproduces it identically)
Then run any Bash tool that produces stdout — Claude Code aborts with the
false ENOSPC.
Suggested fixes (either is sufficient)
- Guard the preflight against bogus/negative values — treat a negative
(or otherwise nonsensical) free figure as "unknown, proceed" rather than
"full". Minimal:
``js…is full…
const K = Math.floor(q.bavail * q.bsize / 1048576);
if (K >= 0 && K < 10) return ; // skip the guard when K < 0`
write()` already surfaces a real ENOSPC if the disk is truly
The actual
full, so a negative preflight reading should never block.
- Fix the underlying
fs.statfstruncation in the bundled runtime so
f_bavail/f_blocks/f_bfree are read as 64-bit (this is a
Node/Bun-level binding issue; report there too).
Workaround (current)
Set CLAUDE_CODE_TMPDIR to a directory on a filesystem with < ~17.6 TB free
(any tmpfs such as /dev/shm qualifies), so the block count stays within
32 bits and the preflight reads a sane value.
4 Comments
**Same preflight, second broken
statfspath: Bun on macOS reportsbsize = 0, so the check fails on any disk size (not just >17.6 TB)**On macOS the installer build of Claude Code is a Bun single-file executable, and Bun's
fs.statfsSyncreturnsbsize = 0for every path on APFS. That makes the preflight computefree MB = bavail × bsize ÷ 1048576 = N × 0 = 0, so it aborts withis full (0MB free)regardless of how much space is actually free. This is independent of the 32-bit truncation case in the original report — it triggers on normal-sized disks too.Repro on macOS 15 (Darwin 24.6), APFS, ~196 GB free:
bsizeis0for every path tried (/,$HOME,/private/tmp, project dir). Still reproduces on Bun 1.3.14 (current latest) — the version embedded in the Claude Code 2.1.159 native binary — so upgrading Bun does not help, and the bundled runtime can't be swapped.Note this is not fixed by the proposed
if (K >= 0 && K < 10)guard, because hereK === 0, not negative. A robust guard needs to treatbsize === 0(or any non-positivebsize) as "unknown — proceed and let the realwrite()surface a genuine ENOSPC", in addition to the negative-value case. Properly readingf_bsize/f_bavailas 64-bit in the Bun binding would fix it at the source.Also worth noting for other macOS users hitting this: setting
CLAUDE_CODE_TMPDIRdoes not help, since Bun returnsbsize=0for all paths.Still present in 2.1.163 — Bun
bsize=0variant is unfixedThe
{bigint: true}+ negative-value guard in 2.1.163 fixes the original >17.6 TB truncation, but @AmirL'sbsize=0variant is still live. Confirmed and reproduced on macOS (Darwin 25.5.0, APFS, 68 GiB free).Root cause chain:
statfsstruct alignment bug (oven-sh/bun#31133) returnsbsize=0on macOS x86_64 APFSbavail * 0n / (1024n * 1024n) = 0n, and0n < 10ntriggers the "0MB free" errorq < 0nguard doesn't catch this since0nis not negativeTrigger condition: any command that exits non-zero with empty stdout (e.g.
grepwith no matches,ls | grepof a missing pattern). The diagnostic runs whenstdoutToFile && stdout === "" && exitCode \!== 0, misattributing the empty output to ENOSPC. The harness kills the process — commands chained after the failure (;,||, etc.) never execute.Observed in the wild on a simple
ls | grep:Suggested fix: add a
bsize <= 0nguard alongside the existing negative check:Workaround for other macOS x86_64 users:
The
BASH_ENVvariable tells bash to source a file before running non-interactive commands (which is how Claude Code spawns them). Set a trap that ensures stdout is never empty on failure:~/.claude/enospc-workaround.sh:~/.bashrc:This has zero impact on successful commands. Remove once Claude Code ships with Bun >= 1.3.15.
Building on @maneeshjupiter's
BASH_ENVworkaround in https://github.com/anthropics/claude-code/issues/63877#issuecomment-4627467164: on stock macOS the trap alone does not work, because a second bug prevents the EXIT trap from ever firing. One extra line makes the workaround reliable. Findings below, verified on 2.1.172 and 2.1.173 (both still embedbun-v1.3.14) on macOS x86_64.Second bug: snapshot shadow functions
execover the wrapper shell on bash 3.2The shell snapshot (
~/.claude/shell-snapshots/snapshot-bash-*.sh) shadowsgrep,find, andrgwith functions dispatching to the embedded ugrep/bfs. Subshell detection looks like:BASHPIDwas added in bash 4.0. macOS/bin/bashis 3.2.57, where it is empty, so[[ $BASHPID != $$ ]]is always true and everygrep/find/rgcall takes theexecbranch, replacing the main wrapper shell. Consequences:BASH_ENVEXIT trap never runs.grep/find(grep foo f && next,grep foo f; next) are silently never executed.pwd -P >| /tmp/claude-*-cwd) never runs, so cwd tracking breaks.bsize=0false ENOSPC ("temp filesystem is full (0MB free)") described in the comment above.Pipelines like
grep foo f | headsurvive because theexechappens inside a pipeline subshell, which makes the failures look intermittent.Updated workaround
On bash 3.2,
BASHPIDis an ordinary assignable variable (it only became special and readonly in 4.0), so it can be supplied fromBASH_ENV. Full~/.claude/enospc-workaround.sh:with
export BASH_ENV="$HOME/.claude/enospc-workaround.sh"in~/.bashrcas before. Result: no more false ENOSPC, chained commands execute again, and the embedded ugrep/bfs still work, since the shadow functions simply take their existing(exec ...)subshell branch.Suggested fixes in Claude Code
[[ -n $BASHPID && $BASHPID != $$ ]], or drop theexecfast path entirely; the subshell branch is always correct.if (q < 0n || A.bsize <= 0n) return null;and/or ship Bun >= 1.3.15.Side note for affected users: the shadowing is skipped when
GreporGlobis passed via--allowedTools/--toolsat launch (it setssearchToolsOptIn), which is another temporary escape hatch that avoids theexecbug entirely (though not the Bun preflight one).This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.