Task runner reports ENOSPC on subprocess output despite disk having free space

Status Open
Maintainer reply None cached
Activity 12 comments · opened May 30, 2026

Bug

Claude Code's Bash tool reports ENOSPC when capturing any subprocess stdout, silently losing all command output. Commands with no output succeed; commands with any output fail.

Error message

Command output was lost: the temp filesystem at /private/tmp/claude-501/<project>/<session-uuid>/tasks is full (0MB free). The child process's stdout/stderr writes failed with ENOSPC.

Environment

  • macOS Darwin 24.6.0
  • Shell: zsh
  • CLAUDE_CODE_TMPDIR=/tmp/claude-501 (already set — resolves to /private/tmp/claude-501 via symlink on macOS)

Diagnostics

  • df -h on the tasks directory: 23 GB free on /dev/disk1s2 (APFS, 90% used)
  • APFS container free: 24.6 GB
  • du -sh /private/tmp/claude-501/: 8 KB (essentially empty)
  • Task .output files are created at 0 bytes — write never completes
  • Direct shell writes to the same directory succeed: echo "test" > .../tasks/test.txt → 5 bytes, readable
  • quota -v → no quota
  • ulimit -f → unlimited
  • No extended attributes on the tasks directory

Key observation

The session UUID is per-conversation — the same UUID appears across multiple Claude Code relaunches within the same conversation. Once the UUID enters this broken state, all subsequent Bash tool invocations in that conversation lose their output. Starting a new conversation gets a new UUID and clears the problem.

Impact

All Bash tool command output is silently lost for the duration of the affected conversation. The tool appears to succeed (exit 0) but returns no output.

Hypothesis

The task runner's subprocess-to-file pipe write is returning ENOSPC at the OS level despite available disk space. Possible causes: (1) incorrect internal "available space" tracking that hits 0 before the real disk does, (2) a pipe buffer issue where the reader isn't draining concurrently with subprocess execution, or (3) a per-session-UUID state corruption.

View original on GitHub ↗

11 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/51814
  2. https://github.com/anthropics/claude-code/issues/63797
  3. https://github.com/anthropics/claude-code/issues/35442

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

kazunorimiura · 3 months ago

Same issue on macOS, Claude Code v2.1.158 (latest on npm). Additional data points:

  • Sandbox is disabled (no sandbox key in any settings.json), so this is not sandbox-overlay related — it happens with the plain task runner.
  • Moving CLAUDE_CODE_TMPDIR to a real disk does not help: pointed it at ~/.claude/tmp (APFS, 178 GiB free, inode usage 0%) and relaunched — same 0MB free ENOSPC under the new path; the tasks dir is empty (ls → total 0).
  • claude --continue does not help (it reuses the same session UUID).
  • Starting a fresh conversation (new UUID) does NOT durably fix it, contrary to the workaround mentioned here. A new session works for the first command or two (builtins like echo pass), then the next external process (rg, ls, git) hits

ENOSPC — and this recurs in every new session. So the corrupted "available space" state isn't merely stuck to one UUID; new UUIDs degrade to the same failure quickly.

  • Builtin-vs-subprocess split confirmed: echo succeeds; fork/exec processes fail; the captured .output is created at 0 bytes.

Version: 2.1.158 / macOS. (Possibly related to the empty-output reports in #63797, though there the failure is silent empty output on Linux rather than an explicit ENOSPC.)

llucian-iws · 3 months ago

Reproduced independently — adding a second data point plus a concrete impact case and a mitigation that helps with one failure mode.

Environment

  • macOS Ventura, Darwin 22.6.0, Intel x86_64 (reporter is on 24.6.0/Sequoia — so this spans at least two macOS major versions and Intel, not just Apple Silicon)
  • zsh, CLAUDE_CODE_TMPDIR in use

Same false-ENOSPC signature

Identical error string:

Command output was lost: the temp filesystem at
/private/tmp/claude-501-<pid>/claude-501/<project>/<session-uuid>/tasks
is full (0MB free). The child process's stdout/stderr writes failed with ENOSPC.

Meanwhile the disk was nowhere near full:

  • diskutil info (the truthful number, incl. purgeable): 27.4 GB free
  • all claude-* temp roots combined: ~10 MB (largest single root 5.7 MB)
  • inodes: 268M free (1% used) — not inode exhaustion either

So this is not real disk exhaustion and not inode exhaustion. Confirming OP's read: it's the task-runner's subprocess-output capture failing at the OS level despite free space.

Confirming intra-session, and the per-conversation-UUID trap

I can corroborate OP's key observation: the broken state is keyed to the per-conversation session UUID and persists across relaunches within the same conversation. The failing tasks dir lived inside a single session's own private root — no other instance could touch it — so for that occurrence it was an intra-session self-race (the harness's own temp cleanup racing its own output write), not a cross-session collision.

(For completeness, the cross-session variant is also real and tracked separately, e.g. #49512 / #25292, where concurrent sessions sharing /tmp/claude-$UID race-delete each other's tasks/. Both classes exist; this issue is the single-session one.)

Why this is worse than "lost output" — it silently dropped file edits that shipped to prod

The most damaging part isn't the missing stdout — it's that the scrambled/empty tool output masked failed Edit calls. In one batch, 4 edits failed (bad old_string / unread-file) but the failures were hidden by the corrupted output, the tool chain reported success, and the change was committed and deployed to production with those 4 edits missing. It was only caught afterward by checking the deployed artifact (a prod sitemap that had 0 of the expected URLs), then fixed in a follow-up commit. So the blast radius of this bug includes silent partial-edits reaching prod, not just cosmetic output loss — which lines up with the "tool appears to succeed (exit 0) but returns no output" impact OP described.

CLAUDE_CODE_TMPDIR to a roomy disk does not fix it

Confirming the May-31 comment: pointing CLAUDE_CODE_TMPDIR at a filesystem with plenty of free space does not resolve it, because a relaunch within the same conversation reuses the same poisoned session-UUID dir.

Partial mitigation that does help (for the relaunch-persistence trap)

A shell wrapper that gives each launch a distinct per-PID temp root outside /tmp stops a relaunch from inheriting a poisoned tasks dir, and moves the scratch dir out of reach of the macOS /tmp reaper:

claude() {
  local d="$HOME/.cache/claude-tmp/$$"
  mkdir -p "$d"
  CLAUDE_CODE_TMPDIR="$d" command claude "$@"
}

This does not stop the race from occurring mid-conversation (that's upstream), but it prevents the "stays broken across relaunch until you start a brand-new conversation" failure mode.

Suggested upstream fix direction

The underlying defect looks like the output-capture open()/write path treating a transient failure (or a cleanup-deleted dir) as ENOSPC and then surfacing it as "0MB free." Two things would sharply reduce blast radius even before the race is fixed:

  1. Don't conflate a failed write with ENOSPC unless statfs actually shows ~0 — report the real errno (often ENOENT from a deleted dir).
  2. Make Edit/Bash failures hard-fail loudly rather than being swallowable by corrupted capture, so a partial edit can't silently pass through to a commit.

Happy to provide more diagnostics if useful.

bbkyus · 3 months ago

One additional data point not yet in the thread: within-session inconsistency. In the same session, git status and cat succeeded while git branch failed with ENOSPC.

wsimf · 3 months ago

Noticed Claude trying to set CLAUDE_CODE_TMPDIR in the middle of the conversation, even that doesn’t seem to fix the issue.

Claude’s diagnosis:

The filesystem is fine — 288 GB free, no quotas, writes work. The error from the previous session appears to have been a false positive or a transient Claude Code bug. Here's what I found:
- Filesystem: 288 GB available, no disk quotas, inodes plentiful - The tasks dir: contains two 0-byte .output files — these are Claude Code's per-command output capture files - Manual write test: writing to one of those files succeeds without issue
The ENOSPC error was misleading. It may have been a transient issue (e.g., another process briefly exhausting /tmp), or a bug in how Claude Code reports the error when a command output file fails to write for a different reason (permissions race, file descriptor issue, etc.).
Pnutbutter94 · 2 months ago

macOS repro (Darwin 23.6.0 / macOS Sonoma)

Hitting this in a long interactive session on macOS — not a Cowork VM.

Setup:

  • macOS Sonoma 23.6.0, M-series Mac, 8GB RAM
  • Claude Code running in terminal (not Cowork)
  • Session involved ~6–8 background Bash task calls over ~45 minutes
  • Tasks dir: /Users/<user>/.claude/tmp/claude-501/-Users-<user>/<session-id>/tasks/

Repro:

  1. Run a long session with multiple background Bash calls (run_in_background: true)
  2. After ~4–6 background tasks, subsequent Bash commands start failing with:

``
Command output was lost: the temp filesystem at .../tasks is full (0MB free).
The child process's stdout/stderr writes failed with ENOSPC.
``

  1. df -h on the tasks dir shows 14GB free on the underlying /dev/disk1s1 filesystem — this is a false ENOSPC on a normal macOS APFS volume, not a tmpfs at all
  2. Deleting stale .output files from the tasks dir (total ~8KB!) unblocks the next command temporarily, but it recurs

Key difference from other repros: This is not a tmpfs or disk-full scenario. The tasks dir sits on a normal APFS volume with gigabytes free. The ENOSPC is internal to how Claude Code manages the tasks subprocess output buffer — the 4 accumulated .output files from completed background tasks total only 8KB yet trigger the error.

Workaround: rm ~/.claude/tmp/claude-501/-Users-<user>/<session-id>/tasks/*.output clears it temporarily.

morrissimo · 2 months ago
macOS repro (Darwin 23.6.0 / macOS Sonoma) Hitting this in a long interactive session on macOS — not a Cowork VM. Setup: macOS Sonoma 23.6.0, M-series Mac, 8GB RAM Claude Code running in terminal (not Cowork) Session involved ~6–8 background Bash task calls over ~45 minutes Tasks dir: /Users/<user>/.claude/tmp/claude-501/-Users-<user>/<session-id>/tasks/

Can confirm same issue observed, on a very similar setup and virtually identical session meta:

  • macOS Tahoe 26.5.1, Intel i9 (2019 era), 32GB RAM
  • Claude Code running in terminal (not Cowork)
jeremyeccles · 2 months ago

Corroborating repro — deterministic, with control tests isolating the exact trigger (matches the statfs bsize=0 analysis in #65166)

Environment: Claude Code 2.1.156, desktop app local-agent mode, macOS Darwin 25.5.0, Intel x86_64, APFS with 105 GiB genuinely free (df and diskutil container-free agree, so no purgeable-space mirage).

The trigger is precisely: empty captured output AND nonzero exit. Each of these is 100% reproducible, not intermittent:

| Command | Output | Exit | Result |
|---|---|---|---|
| false | none | 1 | ❌ "temp filesystem … is full (0MB free) … ENOSPC" every time |
| true | none | 0 | ✅ clean |
| sh -c 'echo hi; exit 1' | stdout | 1 | ✅ output captured fine |
| sh -c 'echo err >&2; exit 3' | stderr | 3 | ✅ output captured fine |
| dd if=/dev/zero of=<tasks dir>/.t bs=1m count=5 | — | 0 | ✅ 5 MB written into the "full" dir at ~1.9 GB/s |

This exactly fits #65166's decompiled handler (stdout === "" && exitCode !== 0explainTmpFailure → Bun fs.statfs returning bsize = 0 on darwin-x64 → computed free = 0 MB → always reports full). Nothing is actually lost — in every reproduced case the "lost" output was genuinely empty.

Impact beyond cosmetics: the message reads as authoritative, so agents (and users) burn real time on phantom disk cleanup. On this machine it drove three separate "disk full" investigation sessions across a week — inventing purgeable-churn theories, deleting VM images, restarting Docker — before controlled tests showed every "ENOSPC" was just a silently-failing command (grep/lsof with no match). Suggested fix until the Bun statfs fix (oven-sh/bun#31139) ships in a release: treat bsize == 0 as "unknown", not "full", and/or verify with a real test write before claiming ENOSPC.

greg-blip-max · 2 months ago

---
Environment:

  • macOS 14/15 (Darwin 24.6.0)
  • Claude Code CLI v2.1.175, TUI fullscreen mode
  • CLAUDE_CODE_TMPDIR set (both /tmp and a custom ~/tmp/claude-code path tried — neither helps)
  • Disk: 322 GB free at 65% capacity on /dev/disk1s1

What happens:
grep with no matches silently fails with a false ENOSPC error every time:

Command output was lost: the temp filesystem at [...]/tasks is full (0MB free).
The child process's stdout/stderr writes failed with ENOSPC.

Exact reproduction:
grep -n "nonexistent_pattern" anyfile.md # → false ENOSPC, exit 1
echo "hello" # → works fine

The pattern is precisely: external binary + no stdout + exit code 1. Shell built-ins are unaffected.

Impact: grep is effectively broken for the common "check if pattern exists, expect no match" case. This affects any Claude Code session doing file search, log inspection, or validation checks. Our session management scripts that use grep are also at risk if they ever hit a no-match condition.

Workaround: Replace grep with a pure-bash while read loop. This is not a reasonable long-term solution for real workflows.

Changing CLAUDE_CODE_TMPDIR does not help — the issue is in the subprocess output capture mechanism, not the filesystem.
---

TelpeNight · 2 months ago

Possible workaround would be to force agent to act differently by hooks. This fixed the problem for me, at least it doesn't interrupt the flow:

    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/false-enospc-detect.sh",
            "timeout": 5
          }
        ]
      }
    ]
#!/usr/bin/env bash

PAYLOAD=$(cat)

RESPONSE=$(printf '%s' "$PAYLOAD" | jq -r '.tool_response.stdout // ""' 2>/dev/null)

if [[ "$RESPONSE" != "Command output was lost"* ]]; then exit 0; fi
if [[ "$RESPONSE" != *"is full (0MB free)"*     ]]; then exit 0; fi
if [[ "$RESPONSE" != *"ENOSPC"*                 ]]; then exit 0; fi

read -r -d '' CONTEXT <<'EOF'
FALSE ENOSPC DETECTED — this is Claude Code bug #65166 (Bun's fs.statfs returns
bsize=0 on x86_64 macOS, so the "disk full" preflight always reports 0MB free
when a Bash command exits non-zero with empty stdout). The disk is NOT full.

Do this — in order:

1. Do not diagnose the temp dir. No df, no mkdir, no rm -rf, no du, no
   ls /private/tmp/claude-*. If the user confirms df shows free space that
   *confirms* this bug, not a real disk issue. HOME is on the same volume so
   CLAUDE_CODE_TMPDIR will not help.

2. Do not guess what the lost output "probably was". The harness destroyed
   it; you don't know if exit was 0, 1, or 137, and you don't know what stderr
   said. find-exit-1 is a real error, not "no results". Capture for real.

3. Rerun the original command through this wrapper (immune to the bug — stdout
   is never empty, final exit is always 0):

       o=$(mktemp); e=$(mktemp); <ORIGINAL_CMD> >"$o" 2>"$e"; ec=$?; \
         echo "exitcode=$ec"; echo "---stdout---"; cat "$o"; \
         echo "---stderr---"; cat "$e"; rm -f "$o" "$e"

   Read exitcode=N and the two captured streams as the real tool result.
EOF

jq -n --arg ctx "$CONTEXT" '{
  hookSpecificOutput: {
    hookEventName: "PostToolUse",
    additionalContext: $ctx
  }
}'

This will instruct agent to retry with success. Works for me.

The more clear approach may be to use updatedToolOutput feature. Implement retry in hook itself -> hijack output. But this needs more debugging. Agent self healing is easier for now and works.

greg-blip-max · 2 months ago

Thanks!

On 06/13/2026 2:02 PM CDT TelpeNight @.***> wrote: TelpeNight left a comment (anthropics/claude-code#63909) https://github.com/anthropics/claude-code/issues/63909#issuecomment-4699485877 Possible workaround would be to force agent to act differently by hooks. This fixed the problem for me, at least it doesn't interrupt the flow: "PostToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "~/.claude/hooks/false-enospc-detect.sh", "timeout": 5 } ] } ] #!/usr/bin/env bash PAYLOAD=$(cat) RESPONSE=$(printf '%s' "$PAYLOAD" | jq -r '.tool_response.stdout // ""' 2>/dev/null) if [[ "$RESPONSE" != "Command output was lost" ]]; then exit 0; fi if [[ "$RESPONSE" != "is full (0MB free)" ]]; then exit 0; fi if [[ "$RESPONSE" != "ENOSPC" ]]; then exit 0; fi read -r -d '' CONTEXT <<'EOF' FALSE ENOSPC DETECTED — this is Claude Code bug #65166 (Bun's fs.statfs returns bsize=0 on x86_64 macOS, so the "disk full" preflight always reports 0MB free when a Bash command exits non-zero with empty stdout). The disk is NOT full. Do this — in order: 1. Do not diagnose the temp dir. No df, no mkdir, no rm -rf, no du, no ls /private/tmp/claude-. If the user confirms df shows free space that confirms this bug, not a real disk issue. HOME is on the same volume so CLAUDE_CODE_TMPDIR will not help. 2. Do not guess what the lost output "probably was". The harness destroyed it; you don't know if exit was 0, 1, or 137, and you don't know what stderr said. find-exit-1 is a real error, not "no results". Capture for real. 3. Rerun the original command through this wrapper (immune to the bug — stdout is never empty, final exit is always 0): o=$(mktemp); e=$(mktemp); <ORIGINAL_CMD> >"$o" 2>"$e"; ec=$?; \ echo "exitcode=$ec"; echo "---stdout---"; cat "$o"; \ echo "---stderr---"; cat "$e"; rm -f "$o" "$e" Read exitcode=N and the two captured streams as the real tool result. EOF jq -n --arg ctx "$CONTEXT" '{ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: $ctx } }' This will instruct agent to retry with success. Works for me. The more clear approach may be to use updatedToolOutput feature. Implement retry in hook itself -> hijack output. But this needs more debugging. Agent self healing is easier for now and works. — Reply to this email directly, view it on GitHub https://github.com/anthropics/claude-code/issues/63909?email_source=notifications&email_token=BWZGVOA7BFHA7RS7WH6SECT47WQLHA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINRZHE2DQNJYG432M4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#issuecomment-4699485877, or unsubscribe https://github.com/notifications/unsubscribe-auth/BWZGVOF2ODQSAF5EYQE37XL47WQLHAVCNFSNUABFKJSXA33TNF2G64TZHM4TGNZSGUZTINZVHNEXG43VMU5TINJVGM4TGNJRGIYKC5QC. Triage notifications, keep track of coding agent tasks and review pull requests on the go with GitHub Mobile for iOS https://github.com/notifications/mobile/ios/BWZGVOE3HVNLSH7QIBL7CDT47WQLHA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINRZHE2DQNJYG432M4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJKTGN5XXIZLSL5UW64Y and Android https://github.com/notifications/mobile/android/BWZGVOA6GZW5CZDPDF3QPW347WQLHA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTINRZHE2DQNJYG432M4TFMFZW63VHMNXW23LFNZ2KKZLWMVXHJLTGN5XXIZLSL5QW4ZDSN5UWI. Download it today! You are receiving this because you commented.Message ID: @.***>

Showing cached comments. Read the full discussion on GitHub ↗