Task output files grow unboundedly, filling entire disk (278 GB in minutes)

Status Open
Reported on v2.1.89
Maintainer reply None cached
Activity 7 comments · opened Apr 1, 2026

Product: Claude Code CLI
Version: 2.1.89
OS: macOS 26.2 (arm64, Apple Silicon)
Severity: Critical — causes complete disk exhaustion, system instability
Reproducibility: Intermittent / not yet reliably reproducible

Summary

Claude Code task output files in /private/tmp/claude-<uid>/ grow to hundreds of gigabytes within minutes, filling the entire disk. A single Claude Code session in one project consumed 278 GB of disk space via two runaway task output files (232 GB and 46 GB). The 926 GB drive was at 99% capacity with only 10 GB remaining.

Steps to Reproduce

Not yet reliably reproducible. The conditions under which this occurred:

  1. Using Claude Code 2.1.89 with Opus 4.6 (1M context)
  2. Working in a project with multiple subagent types configured (custom plugin agents and built-in core agents)
  3. Session involved multiple background tasks / subagents — the tasks/ directory contained 61 symlinks to subagent output .jsonl files spanning multiple session IDs, suggesting a shared/aggregated task output directory across sessions
  4. Two task output files grew without bound until the disk was full
  5. Files were still actively growing at the time of discovery

What the output files contain

Inspecting the file content:

  • Head of file: Normal JSONL subagent message records (assistant messages, tool use, etc.), each line containing a JSON object with fields like parentUuid, agentId, message, sessionId, etc.
  • Tail of file: Claude Code's own minified JavaScript bundle source code — internal implementation details including session management, git worktree listing, AbortController logic, stream processing, etc. This is clearly not intended output.

This suggests Claude Code may be writing its own bundle contents into the task output stream, possibly repeatedly.

Observed data

Location: /private/tmp/claude-<uid>/<project-path>/<session-id>/tasks/

File 1:  232 GB  created 21:11:00  modified 21:23:13  (232 GB in ~12 min, ~320 MB/s)
File 2:   46 GB  created 21:13:05  modified 21:14:54  ( 46 GB in  ~2 min)

Disk state at discovery:

$ df -h /System/Volumes/Data
Filesystem       Size    Used   Avail Capacity
/dev/disk3s5     926Gi   889Gi   10Gi    99%

$ du -sh /private/tmp/claude-<uid>/
278G    /private/tmp/claude-<uid>/

Impact

  • Complete disk exhaustion — 926 GB drive filled to 99% capacity (10 GB free)
  • no space left on device errors across the system
  • All writes on the system were failing, affecting unrelated applications
  • The files were still actively growing at time of discovery

Expected Behavior

Task output files should have a size cap or rotation policy. A single task output should never be able to consume more than a reasonable amount of disk space. Potential safeguards:

  • Maximum file size limit for task output
  • Periodic cleanup of stale temp files
  • Monitoring/warning when temp directory usage exceeds a threshold
  • Output stream deduplication (the same content should not be written repeatedly)
  • The Claude Code JS bundle should never end up in a task output stream

Workaround

Deleting the temp directory resolved the issue in this case:

rm -rf /private/tmp/claude-$(id -u)/

Claude Code recreates what it needs on next launch.

Environment

  • Claude Code: 2.1.89
  • Model: claude-opus-4-6 (1M context)
  • macOS: 26.2 (Build 25C56)
  • Architecture: arm64 (Apple Silicon)
  • Disk: 926 GB APFS volume
  • Configuration: Custom plugin agents, built-in agents, and an MCP server; multiple subagent types active

View original on GitHub ↗

7 Comments

pwelty · 4 months ago

Hitting this too. Two incidents in 36 hours on macOS with Claude Code orchestrated by Opus 4.7 (1M context) dispatching Sonnet subagents.

Incident 1 — 2026-04-18: Dispatched 4 parallel background subagents (model: sonnet) for dev work across 4 different repos. One went runaway and consumed ~65 GB in /private/tmp/claude-501/<session>/tasks/<taskid>.output before I noticed. Forced restart, lost work from 3 sibling subagents.

Incident 2 — 2026-04-19: After the first incident I added explicit output-bounding instructions to every dispatch prompt ("do NOT cat files >500 lines, use head -200/tail -200, --max-count=50 on grep/find, no recursive dumps, compact-reporter for Playwright"). Re-dispatched 3 parallel subagents with these bounded prompts. One hit ~71 GB, locked my 24 GB RAM machine, forced another restart.

Key observations:

  • Prompt-level output bounds are not enforcement — the subagent ignored them.
  • Signal file path is exactly /private/tmp/claude-501/<session>/tasks/*.output, matching this issue and #39909, #35121, #26911, #42125.
  • Correlation (not causation) with Opus 4.7 rollout: months of clean parallel-dispatch workflows under 4.6, then hit this twice in 36 hours under 4.7. 4.7 release notes say it "spawns fewer subagents by default" — may simply mean fewer chances to trigger the underlying bug, not that the bug is fixed.
  • Total downtime: ~90 minutes over 2 days. Not recoverable via /clear or compaction; requires a machine restart because the disk fills during an active Claude Code process.

Mitigation I'm building (since this is unfixed): external launchd watchdog that monitors /private/tmp/claude-*/*/tasks/*.output and truncates any file growing past 1 GB, logs a kill event, and alerts me. This is a user-space band-aid — the real fix is Claude Code capping task-output capture internally.

Happy to share transcripts if they'd help reproduce.

Chrisart27 · 3 months ago

Hitting this on Linux too, with a failure mode worth flagging as a new variant within the same root cause.

2026-05-20, Claude Code 2.1.145, Ubuntu 24.04, Opus 4.7 (1M context), parallel-Bash subagent workflow. A foreground subagent ran a Bash command that globbed across its own session's task-output directory (/tmp/claude-1000/<project>/<session>/tasks/*.output), intending to inspect sibling subagents' captured output. The harness was simultaneously writing the new Bash command's stdout into the same directory under a freshly-allocated UUID. The glob picked up the in-flight capture file, the running command started reading what the harness was writing for it, and a classic cat target > target feedback loop began at the harness layer. The file grew at ~18 MB/s for ~45 minutes until it hit 48 GB and the session was abandoned.

Head and tail of the 48 GB file confirm self-recursion. Both ends show the same chunk repeating endlessly: a list of sibling .output filenames followed by === test session starts ===, then the same list again, forever. Effectively for f in *.output; do echo "=== $f ==="; head $f; done where one of the $fs expanded to the loop's own stdout-capture file.

Symptomatic blast radius matched #42461 and #51814 exactly. /tmp is a 61 GB tmpfs on this box. Once the runaway file pushed /tmp to 80 percent, every other Claude Code session for the same user across multiple tmux panes started returning exit 1 with empty captured output, because the harness sentinel write was contending on the pressured tmpfs. The user's other parallel work was effectively dead until the runaway file was located and removed. rm of the single 48 GB file dropped /tmp back to 2 percent and all sessions recovered.

This variant matters because prompt-level output bounding (per pwelty's incident 2 comment above) cannot prevent it. The command in question was intentionally finite, used head and tail, capped its grep results — every guardrail the OP-style runaway would suggest. The agent had no way to know that one of its glob results was the in-flight capture file for the very command it was about to run, because that file's existence and path are harness-internal state the agent doesn't see. A user-space watchdog truncating any .output past 1 GB would catch it. A harness-side output cap remains the right fix and would cover the OP, pwelty's incidents, and this self-recursion variant in one change.

A cheap incremental detection path that would specifically eliminate the self-recursion class: when the harness is about to launch a Bash command, refuse or short-circuit if any path resulting from glob expansion of the command's argv equals the file the harness is about to write that command's stdout to. Two-line check, catches the entire self-recursion class without affecting any sane Bash command. Happy to share the full file contents (it's 48 GB of the same fragment repeating, so a head -c 4096 and tail -c 4096 capture the whole signal) if a maintainer wants ground truth.

80r1an · 3 months ago

Confirming this also happens on Windows, same unbounded .output growth, but with a concrete trigger that might help reproduce it.

The output file lives here on Windows:
%LOCALAPPDATA%\Temp\claude\<project>\<session>\tasks\<id>.output

Trigger (possible repro): I accidentally typed !go t upeux (a typo) in bash mode. Note that go is not installed on my machine / not on PATH, so I'd expect a plain command not found and an immediate exit. Instead, the background task entered an infinite loop and kept appending the same input string (go t upeux) to its .output file. It grew completely unbounded until it hit ~65 GB and filled my C: drive to 0 bytes free, destabilizing the system.

Slightly different tail content from the original report: in my case the file is just the input string repeated, not the minified JS bundle, but it looks like the same root cause (task output stream looping/writing without any size cap).

Repro:

  1. In bash mode (! prefix), run a command whose binary does not exist, e.g. !go t upeux
  2. The background task loops and keeps appending to its .output file
  3. File grows with no size cap → disk saturation (65 GB in my case)

Expected:

  • A non-existent command should fail fast (command not found), not loop
  • Task .output files should have a hard size cap / rotation as a safety net
  • Ideally: detect repetitive looping output and abort the task

Env: Claude Code on Windows, go not installed.

hayatoy98 · 3 months ago

Additional analysis: Root cause identified — self-referential feedback loop in output aggregation

Environment: Claude Code 2.1.50, claude-haiku-4-5-20251001, macOS

Findings

The parent task's output file (bc90866.output) contains itself recursively:

  • First 100MB alone: === bc90866.output === section delimiter appears 16,059 times
  • At ~350GB mark: still 1,689 occurrences per 10MB
  • At ~600GB mark: same database structure report still repeating
  • Final file size: 724,631,901,757 bytes (~675GB) before ENOSPC

Root cause

When aggregating sub-agent outputs into the parent task's output file, the aggregation mechanism includes the parent's own output file as a source, creating an exponential feedback loop:

  1. Parent task bc90866 spawns 14 child agents
  2. Child agent outputs are aggregated into bc90866.output
  3. During aggregation, bc90866.output itself is re-read and re-appended as input
  4. Each aggregation cycle roughly doubles the file size
  5. Loop only terminates when the disk is full (no space left on device at EOF)

The child agent (ad5ef92e) that triggered this was generating a database structure report. The task itself completed successfully — the bug is purely in the output aggregation mechanism.

Suggested fix

The aggregation logic should exclude the parent task's own output file from the set of files it reads when collecting sub-agent outputs. A simple filename/path check against the destination file before reading sources would prevent this loop entirely.

Fame510 · 2 months ago

This is the exact class of failure that pre-execution circuit breakers prevent — and it's why we built SHACKLE.

The core issue: Claude Code has no bound on output size because there's no pre-execution budget check. A single runaway task writes 278GB to disk because nothing says "stop" until the disk is full.

What would prevent this:

  1. Per-task output budget — cap total bytes written per task invocation, enforced BEFORE the write, not after.
  2. Tool-call deduplication — if the same file-write call fires 1000x in a session, it's a loop regardless of what the model thinks.
  3. Wall-clock timeout per invocation — a task writing 278GB in "minutes" should have been killed at 30s.

We open-sourced SHACKLE with these exact mechanisms — it's a process-level guard that sits between the agent and execution: https://github.com/Fame510/SHACKLE-PRO-

The decide() core is <200 lines of Python and could be integrated as a pre-execution hook in Claude Code's tool dispatch path.

Happy to discuss integration if the team is interested.

PaperPrototype · 1 month ago

Same issue here. I noticed claude is writing to /private/tmp/claude-<uid>/... and I managed to find this after searching

Fame510 · 1 month ago

This is a clean example of the runtime-boundary gap — a tool call (disk write / unbounded output) that executes to completion because nothing adjudicates it before it runs. The fix pattern is a pre-execution release-authority check that fails closed: the runtime evaluates the call against declared limits (output size, disk budget, repeat count) and blocks it instead of discovering the damage afterward.

That's the exact model in SHACKLE / SP/1.0 — deterministic decide() before execution, with runnable fixtures you can check against your own repro: github.com/Fame510/SHACKLE. Happy to map a Claude Code disk/output-budget guard to the SP/1.0 vectors if it's useful to the maintainers here — it's a concrete, testable boundary rather than a post-hoc cleanup.