Summary
scripts/session-lifecycle-hook.mjs appends its three export lines to CLAUDE_ENV_FILE on every SessionStart event without checking whether they are already present. Claude Code re-fires SessionStart on every session resume and compaction, so in a long-running session the env file grows without bound.
Because Claude Code prepends the entire env file to every Bash tool invocation, the accumulated duplicates eventually push the composed bash -c string past the Windows 8191-character command-line limit. The command gets truncated mid-token, and every Bash call longer than a few hundred bytes fails with:
/usr/bin/bash: -c: line 70: unexpected EOF while looking for matching `''
The failures look random (long gh pr create commands, heredocs, etc. fail; short commands succeed), which makes this very hard to diagnose from the outside.
Observed impact (real session, plugin v1.0.6, Windows 11)
~/.claude/session-env/<session-id>/sessionstart-hook-1.sh contained the same 3 export lines duplicated 23× (~7.4 KB) after a long session with many compactions/resumes.
- Measured via
echo ${#BASH_EXECUTION_STRING}: a 103-character command produced an 8042-character bash -c string (lines 1–69 of it were the duplicated exports).
- Remaining budget for the actual command was ~250 characters; anything longer was truncated at the 8191-char boundary → shell syntax error.
- Bisected empirically: an all-ASCII
true "AAA…" probe passed at ~360 total bytes and failed at ~400, consistent with the 8191 limit given the ~7.9 KB prefix.
Root cause
appendEnvVar() in scripts/session-lifecycle-hook.mjs:
function appendEnvVar(name, value) {
if (!process.env.CLAUDE_ENV_FILE || value == null || value === "") {
return;
}
fs.appendFileSync(process.env.CLAUDE_ENV_FILE, `export ${name}=${shellEscape(value)}\n`, "utf8");
}
handleSessionStart() calls this for CODEX_COMPANION_SESSION_ID, CODEX_COMPANION_TRANSCRIPT_PATH, and CLAUDE_PLUGIN_DATA on each SessionStart — including source: "resume" / "compact" re-fires — with no dedup.
Suggested fix (verified locally)
Make the append idempotent:
function appendEnvVar(name, value) {
if (!process.env.CLAUDE_ENV_FILE || value == null || value === "") {
return;
}
const line = `export ${name}=${shellEscape(value)}\n`;
try {
if (fs.readFileSync(process.env.CLAUDE_ENV_FILE, "utf8").includes(line)) {
return;
}
} catch {
// file may not exist yet; fall through to append
}
fs.appendFileSync(process.env.CLAUDE_ENV_FILE, line, "utf8");
}
Verified by invoking the hook three times with the same SessionStart payload against a scratch CLAUDE_ENV_FILE: the file stays at exactly 3 lines (unpatched: 9).
An alternative would be to rewrite the file wholesale on each SessionStart (the three values are session-scoped constants), which also self-heals files already bloated by previous plugin versions.
Environment
- Plugin: codex@openai-codex 1.0.6 (marketplace refreshed; 1.0.6 is current latest)
- Claude Code on Windows 11 (Git Bash as the Bash tool shell — the 8191-char limit is what turns the unbounded growth into hard failures, but the growth itself is platform-independent)
Summary
scripts/session-lifecycle-hook.mjsappends its threeexportlines toCLAUDE_ENV_FILEon everySessionStartevent without checking whether they are already present. Claude Code re-firesSessionStarton every session resume and compaction, so in a long-running session the env file grows without bound.Because Claude Code prepends the entire env file to every Bash tool invocation, the accumulated duplicates eventually push the composed
bash -cstring past the Windows 8191-character command-line limit. The command gets truncated mid-token, and every Bash call longer than a few hundred bytes fails with:The failures look random (long
gh pr createcommands, heredocs, etc. fail; short commands succeed), which makes this very hard to diagnose from the outside.Observed impact (real session, plugin v1.0.6, Windows 11)
~/.claude/session-env/<session-id>/sessionstart-hook-1.shcontained the same 3 export lines duplicated 23× (~7.4 KB) after a long session with many compactions/resumes.echo ${#BASH_EXECUTION_STRING}: a 103-character command produced an 8042-characterbash -cstring (lines 1–69 of it were the duplicated exports).true "AAA…"probe passed at ~360 total bytes and failed at ~400, consistent with the 8191 limit given the ~7.9 KB prefix.Root cause
appendEnvVar()inscripts/session-lifecycle-hook.mjs:handleSessionStart()calls this forCODEX_COMPANION_SESSION_ID,CODEX_COMPANION_TRANSCRIPT_PATH, andCLAUDE_PLUGIN_DATAon eachSessionStart— includingsource: "resume"/"compact"re-fires — with no dedup.Suggested fix (verified locally)
Make the append idempotent:
Verified by invoking the hook three times with the same
SessionStartpayload against a scratchCLAUDE_ENV_FILE: the file stays at exactly 3 lines (unpatched: 9).An alternative would be to rewrite the file wholesale on each
SessionStart(the three values are session-scoped constants), which also self-heals files already bloated by previous plugin versions.Environment