[BUG] Hook output and tool-result capture files in /tmp/claude-{uid}/<cwd>/tasks/ accumulate ~19MB null padding per file (missing ftruncate)

Resolved 💬 3 comments Opened May 7, 2026 by hotak92 Closed May 10, 2026

Environment

  • OS: Linux 6.17 (Ubuntu 24.04)
  • Claude Code: VS Code extension (anthropic.claude-code 2.1.131-linux-x64)
  • Auth: claude.ai OAuth (subscription)
  • maxOutputTokens: was 128000 (now lowered to 32000 as a workaround)

Summary

Files written to /tmp/claude-{uid}/<cwd-slug>/<session-id>/tasks/hook_<pid>.output (the path used to capture hook stdout that exceeds the documented 10K char inline cap, and the same path used for large tool-subagent results per #26911) are observed at ~19,562,240 bytes each, of which only ~9 KB is the actual captured output. The remainder is a contiguous tail of NULL bytes.

Math: 19,562,240 / 128,000 ≈ 153 bytes per maxOutputToken — strongly suggests the runtime pre-allocates a buffer sized to maxOutputTokens × bytes-per-token-upper-bound, then writes the actual (much smaller) captured output without subsequently calling ftruncate(fd, real_size) or opening with O_TRUNC. This is the same bug class documented in #51435 for the skill-upload writer.

Reproduction

  1. Configure a SessionStart:compact hook that emits >10K chars of stdout (e.g., the user's compact-context-reinject.sh which cats a CONTEXT_STATE.md-style file).
  2. Use Claude Code in the project for several sessions.
  3. ```bash

du -sh /tmp/claude-$(id -u)/-home-*/tasks/
```
Accumulates ~19 MB per stdout-capturing hook fire (~80–100 MB per project per session in heavy use).

  1. ```bash

od -c /tmp/claude-$(id -u)/<project>/<session>/tasks/hook_<pid>.output | head -5
``
Confirms NULL prefix (
\\0 \\0 \\0 ...`).

  1. ```bash

stat <file>
``
Blocks * 512 ≈ apparent size` — file is real on disk, NOT sparse.

Local file shape (verified on user's machine):

0000000  \0  \0  \0  \0  \0  \0  \0  \0  ...   (continues for ~19.55 MB)
*
0112520374   m   (   p   e   r   -   s   t   e   p ...   (real stdout starts at offset ~19.55 MB)
0112526337

File size: 19,570,940 bytes. Real content: last ~8.7 KB. Prefix: ~19.55 MB of NULLs.

Impact

  • Disk leak: ~19 MB per stdout-capturing hook fire; 80–100+ MB per project per session.
  • Compounds existing #26911 (no cleanup of tasks/.output files at all).
  • Possibly contributes to GUI freeze symptoms of #23053 / #51560 when the runtime later reads these files back for context injection (extra MB of NULLs to scan/skip).

Suggested fix

After the captured output is written and before the file is closed, call ftruncate(fd, actualBytesWritten). Alternatively open with O_TRUNC on each new capture. Same fix shape proposed for #51435.

Relation to other issues

  • Same path as #26911 (no cleanup) — fixing this would not remove the need for cleanup, but it would reduce per-file size by ~99.95%, making the existing accumulation far less harmful.
  • Same bug class as #51435 (missing ftruncate in another writer).
  • Possibly contributes to symptoms of #23053 / #51560 (large-payload streaming freeze) by inflating bytes the runtime later re-reads from these files into the chat stream.

Workaround we deployed locally

SessionStart/Stop hook that runs find /tmp/claude-$(id -u) -path '*/tasks/hook_*.output' -size +1M -delete. Real hook stdout is always tiny so the 1 MB threshold is safe.

Additional observation

The user's project-level maxOutputTokens: 128000 may be unusually high (default is lower). Lowering it to 32000 reduces the per-file disk waste by 4×. Worth documenting that this setting affects disk usage in /tmp until the underlying truncation bug is fixed.

---

Investigation report (private to user, but happy to share excerpts): the bloated null-padded files were initially suspected to cause the "Transmuting…" GUI freeze, but on closer reading the freeze itself is more likely the streaming-time main-thread stall already documented in #23053 / #51560. The disk leak documented here is a separate, real bug.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗