Task output files grow unboundedly, filling entire disk (278 GB in minutes)
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:
- Using Claude Code 2.1.89 with Opus 4.6 (1M context)
- Working in a project with multiple subagent types configured (custom plugin agents and built-in core agents)
- Session involved multiple background tasks / subagents — the
tasks/directory contained 61 symlinks to subagent output.jsonlfiles spanning multiple session IDs, suggesting a shared/aggregated task output directory across sessions - Two task output files grew without bound until the disk was full
- 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,
AbortControllerlogic, 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 deviceerrors 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
7 Comments
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>.outputbefore 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=50on 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:
/private/tmp/claude-501/<session>/tasks/*.output, matching this issue and #39909, #35121, #26911, #42125./clearor 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/*.outputand 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.
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 classiccat target > targetfeedback 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
.outputfilenames followed by=== test session starts ===, then the same list again, forever. Effectivelyfor f in *.output; do echo "=== $f ==="; head $f; donewhere 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.
rmof 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
headandtail, 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 4096andtail -c 4096capture the whole signal) if a maintainer wants ground truth.Confirming this also happens on Windows, same unbounded
.outputgrowth, but with a concrete trigger that might help reproduce it.The output file lives here on Windows:
%LOCALAPPDATA%\Temp\claude\<project>\<session>\tasks\<id>.outputTrigger (possible repro): I accidentally typed
!go t upeux(a typo) in bash mode. Note thatgois not installed on my machine / not on PATH, so I'd expect a plaincommand not foundand an immediate exit. Instead, the background task entered an infinite loop and kept appending the same input string (go t upeux) to its.outputfile. 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:
!prefix), run a command whose binary does not exist, e.g.!go t upeux.outputfileExpected:
command not found), not loop.outputfiles should have a hard size cap / rotation as a safety netEnv: Claude Code on Windows,
gonot installed.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:=== bc90866.output ===section delimiter appears 16,059 timesENOSPCRoot 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:
bc90866spawns 14 child agentsbc90866.outputbc90866.outputitself is re-read and re-appended as inputno space left on deviceat 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.
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:
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.
Same issue here. I noticed claude is writing to /private/tmp/claude-<uid>/... and I managed to find this after searching
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.