openai / openai/codex-plugin-cc
SessionStart hook appends duplicate exports to CLAUDE_ENV_FILE until every Bash call fails
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 33.3k
- Forks
- 2.3k
- PR merge metrics
- No merged PRs in 30d
Description
SessionStart hook appends duplicate exports to CLAUDE_ENV_FILE until every Bash call fails
Plugin: codex 1.0.5 (marketplace openai-codex, commit 80c31f9)
Platform: Windows 10 Pro 19045, Claude Code, Git Bash (/usr/bin/bash)
Summary
handleSessionStart appends three export lines to $CLAUDE_ENV_FILE unconditionally. SessionStart fires again on every resume, clear, and compact, so a long-lived session accumulates the same three lines over and over. Claude Code inlines that file into the bash -c prelude for every Bash tool call, and the prelude is truncated past roughly 8 KB. The cut lands mid-string inside a quoted export, so from that point on every Bash invocation in that session fails:
/usr/bin/bash: -c: line 84: unexpected EOF while looking for matching `''
echo ok included. The failure is total and permanent for the affected session — the truncation offset is fixed, so the reported line number never changes and no command is small enough to avoid it.
Cause
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");
}
function handleSessionStart(input) {
appendEnvVar(SESSION_ID_ENV, input.session_id);
appendEnvVar(TRANSCRIPT_PATH_ENV, input.transcript_path);
appendEnvVar(PLUGIN_DATA_ENV, process.env[PLUGIN_DATA_ENV]);
}
hooks/hooks.json registers SessionStart with no matcher, so it runs for startup, resume, clear and compact alike. session_id, transcript_path and CLAUDE_PLUGIN_DATA are all constant for the life of a session, so every fire after the first writes bytes that were already there.
Reproduction
Resume or compact a session ~28 times (≈286 bytes per fire on this setup; the threshold is bytes, not fires, so longer paths get there sooner). Then any Bash tool call fails as above.
Observed on this machine — ~/.claude/session-env/<session-id>/sessionstart-hook-3.sh:
201 lines, 3 unique, 19162 bytes # 67 SessionStart fires, broken
90 lines, 3 unique, 8490 bytes # broken
87 lines, 3 unique, 8555 bytes # broken
45 lines, 3 unique, 4440 bytes # still under the limit, works
3 lines, 3 unique, 286 bytes # fresh session, works
Eleven of the session-env files on disk had duplicates; the three over ~8 KB were the ones with dead Bash.
Why it is hard to diagnose
The error names bash and a line number in a string the user never wrote, so it reads as a broken shell profile or a broken PreToolUse hook. It is also session-scoped and correlates with session age, so it looks project-specific or intermittent. Nothing in the plugin appears in the error.
Suggested fix
Make appendEnvVar 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 {
// No env file yet (or unreadable) — fall through and write it.
}
fs.appendFileSync(process.env.CLAUDE_ENV_FILE, line, "utf8");
}
Verified locally against the real hook with a real CLAUDE_ENV_FILE: five identical SessionStart fires produce 3 lines instead of 15; a subsequent fire with a different session_id correctly appends the two changed exports and skips the unchanged CLAUDE_PLUGIN_DATA; the resulting file sources cleanly under set -e.
Alternatives, if you prefer: rewrite the file rather than append (the values are per-session constants, so there is nothing to accumulate), or gate the hook on matcher: "startup" — though that would drop the exports for resumed sessions.
Workaround for anyone hitting this now
Deduplicate the file; content is preserved, since every line is a duplicate of one of three:
f=~/.claude/session-env/<session-id>/sessionstart-hook-3.sh
awk '!seen[$0]++' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
Takes effect on the next Bash call — no restart needed.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in scripts/session-lifecycle-hook.mjs by reading appendEnvVar and handleSessionStart, then inspect hooks/hooks.json to confirm the SessionStart triggers. Reproduce with a real CLAUDE_ENV_FILE and repeated fires; done means repeated identical values do not add duplicate export lines, changed values are handled, and the resulting file remains sourceable.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100