[BUG] SessionStart hook env files (sessionstart-hook-N.sh) accumulate duplicate lines on re-run — never truncated; can escalate to null-byte Bash tool failure on Windows
Environment
- Claude Code version: 2.1.170 (also observed on 2.1.167–2.1.169)
- OS: Windows 11 (hooks run via Git Bash / MSYS2)
- Reproducible: yes, persistent across versions
Description
Hooks that append to $CLAUDE_ENV_FILE (the documented API for persisting env vars from SessionStart hooks) write correctly. However, Claude Code never clears/truncates the per-session env files (~/.claude/session-env/<session-id>/sessionstart-hook-N.sh) when SessionStart hooks re-run within the same session (resume, /clear, compact, etc.). Each re-run appends the same lines again, so the file accumulates duplicates without bound.
Evidence
Scanned all 322 ~/.claude/session-env/<session-id>/ directories on one machine:
- 38 files contain duplicated lines (sessions from 2026-05-12 through 2026-06-10)
- Worst case: the same
exportline repeated 13× in a singlesessionstart-hook-N.sh - Reproduced today on 2.1.170: two sessions show 2× duplicates from a marketplace plugin's SessionStart hook that has no idempotency guard
Example of an affected file (sanitized):
export MY_PLUGIN_DATA='C:/Users/<user>/.claude/plugins/data/<plugin>'
export MY_PLUGIN_SESSION_ID='<session-id>'
export MY_PLUGIN_DATA='C:/Users/<user>/.claude/plugins/data/<plugin>'
export MY_PLUGIN_SESSION_ID='<session-id>'
... (repeated up to 13×)
Impact
The accumulated content is prepended to every Bash tool invocation's preamble. In a severe case observed on 2.1.167 (12×+ accumulation across multiple hooks), Node.js spawn() failed with:
The argument 'args[2]' must be a string without null bytes. Received "export TEMP='...'..."
After that point the Bash tool was 100% unusable for the rest of the session (every invocation failed). The null bytes appear to be introduced when the oversized accumulated preamble goes through the Windows UTF-16→UTF-8 conversion path.
Expected behavior
sessionstart-hook-N.sh files should be truncated (or deduplicated) each time the corresponding SessionStart hook runs — re-running a hook should replace its prior contribution, not append to it.
Workaround
Plugin authors can add an idempotency guard before writing:
if ! grep -q "^MY_FLAG=" "${CLAUDE_ENV_FILE:-/dev/null}" 2>/dev/null; then
echo "MY_FLAG=1" >> "${CLAUDE_ENV_FILE:-/dev/null}" || true
fi
This works, but every plugin author shouldn't need to know this — and third-party plugin hooks without the guard still pollute the session (which is how the duplicates keep appearing on 2.1.170).