SessionStart hook appends duplicate exports to CLAUDE_ENV_FILE on every start, resume and compaction, so every Bash call fails with E2BIG in long sessions
Nobody has claimed this yet.
- #818 by @azixxxxx — closed without merging
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript, node.js
- Domain
- cli, developer-experience, tooling
Research direction
The bug is in scripts/session-lifecycle-hook.mjs. Start by reading the appendEnvVar function (lines 35-40) and handleSessionStart (lines 77-81). The fix involves reading the existing file to check for duplicates before appending. Test by enabling the plugin, starting a session, and verifying the env file doesn't grow after multiple starts/resumes. Run the reproduction steps to confirm the fix.
Written by the indexing model from the issue text.
Description
Summary
scripts/session-lifecycle-hook.mjs appends three export lines to $CLAUDE_ENV_FILE each time SessionStart fires, with no de-duplication. Claude Code fires SessionStart again, under the same session id, on resume and after every compaction. In a long session the file therefore fills with hundreds of identical copies.
Claude Code inlines that env file into the single bash -c argument of every Bash command. Once the argument passes Linux's 128 KiB per-argument limit (MAX_ARG_STRLEN), every Bash and Monitor call fails before it starts, and so does every sub-agent's shell. The session loses its shell completely, and restarting does not recover it, because resuming re-reads the bloated file and adds three more lines.
Environment
- codex plugin 1.0.6 (
codex@openai-codex, fromopenai/codex-plugin-cc) - Claude Code 2.1.281
- Linux 7.0.0-29-generic, GNU bash 5.3.9
getconf ARG_MAX= 2097152. The limit hit is the 128 KiB per-argument cap, not ARG_MAX.
Evidence
1. The error. Every Bash call failed with this, including echo ok:
Could not start /bin/bash: the command line plus environment exceed the OS exec argument limit (E2BIG).
At spawn: command line 133KB across 3 args (largest single arg 133KB); environment 3.3KB across 47 vars (largest: LS_COLORS at 1.7KB).
The environment is only 3.3 KB. Almost all of the ~133 KB is the one bash -c argument.
2. The argument grew steadily through the session. Sizes from successive errors: 128.1 → 128.2 → 129.1 → 131.8 → 131.9 → 132.1 → 132.8 → 133.0 → 133.3 → 133.5 KB. At first only long commands failed; once the base passed 128 KiB, every command did. The jump over a restart and resume (133.0 → 133.3 KB, about 0.3 KB) matches one appended block of three lines (~311 bytes).
3. The env file held the same three lines, repeated. ~/.claude/session-env/<session-id>/sessionstart-hook-0.sh:
export CODEX_COMPANION_SESSION_ID='<session-id>'
export CODEX_COMPANION_TRANSCRIPT_PATH='~/.claude/projects/<project>/<session-id>.jsonl'
export CLAUDE_PLUGIN_DATA='~/.claude/plugins/data/codex-openai-codex'
export CODEX_COMPANION_SESSION_ID='<session-id>'
export CODEX_COMPANION_TRANSCRIPT_PATH='~/.claude/projects/<project>/<session-id>.jsonl'
export CLAUDE_PLUGIN_DATA='~/.claude/plugins/data/codex-openai-codex'
... (the same triplet, over and over)
The session transcript has 136 compaction boundaries, and each is followed by a SessionStart.
4. Trimming the file fixed it. I reduced the file to one triplet (3 lines) and restarted with claude --resume. echo ok then succeeded.
5. It keeps growing after the fix. Shortly after the trim, the file already held 9 lines, 3 copies of each variable (sort | uniq -c):
3 export CLAUDE_PLUGIN_DATA='~/.claude/plugins/data/codex-openai-codex'
3 export CODEX_COMPANION_SESSION_ID='<session-id>'
3 export CODEX_COMPANION_TRANSCRIPT_PATH='~/.claude/projects/<project>/<session-id>.jsonl'
6. Causes ruled out. Disabling the sandbox for the call, deleting large scratch directories, and a plain restart each left the argument size unchanged. No settings file configures a sandbox or deny list, and the user's .bashrc/.profile are stock.
Root cause
scripts/session-lifecycle-hook.mjs:
- lines 35-40:
appendEnvVar()callsfs.appendFileSyncwithout de-duplicating; - lines 77-81:
handleSessionStart()calls it three times on everySessionStart, whatever itssource.
Steps to reproduce
- Enable the codex plugin.
- Start a session, then resume it (
claude --resume) or let it compact, several times. - Run
sort ~/.claude/session-env/<session-id>/sessionstart-hook-0.sh | uniq -c. The count goes up by one perSessionStart. - At about 420 copies (~128 KiB), every Bash call fails with E2BIG.
Expected
Each variable is exported once per session, whatever number of times SessionStart fires.
Suggested fix
function appendEnvVar(name, value) {
const file = process.env.CLAUDE_ENV_FILE;
if (!file || value == null || value === "") {
return;
}
const line = `export ${name}=${shellEscape(value)}`;
const existing = fs.existsSync(file) ? fs.readFileSync(file, "utf8").split("\n") : [];
if (existing.includes(line)) {
return;
}
fs.appendFileSync(file, `${line}\n`, "utf8");
}
An alternative is to export only when the hook input's source is startup. De-duplicating is more robust, because it also covers a changed value on resume.
Workaround
Overwrite ~/.claude/session-env/<session-id>/sessionstart-hook-0.sh with one copy of the three lines, then restart Claude Code and resume the session. The file is read at session start.
- Dominant language
- JavaScript
- Stars
- 34.2k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
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.
More from openai/codex-plugin-cc
-
Review context trims the first column of git status --shortPossibly taken @azixxxxx claimed this 1 day ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
openai/codex-plugin-cc#813 ·
-
codex-rescue: pinned `model: sonnet` triggers "restricted by your organization" fallbackPossibly taken @jcpoyser claimed this 11 days ago. Open
Difficulty 1/5 Under an hour Newbie friendliness 88/100
openai/codex-plugin-cc#804 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openai/codex-plugin-cc#798 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
openai/codex-plugin-cc#794 ·
-
task hangs forever at "Starting Codex task thread." — shorten() splits a UTF-16 surrogate pair in the thread name, app-server drops thread/name/set without replyingPossibly taken @SammyTourani claimed this 11 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openai/codex-plugin-cc#793 ·
All issues in openai/codex-plugin-cc
Similar issues
-
documentation
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
andromarces/agent-loops#886 ·
Maintainers usually reply within 1 day
-
bug effort:low model:light plan planner:gpt-6-1-sol tests
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
B: visual
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
FreeTubeApp/FreeTube#9929 ·
Maintainers usually reply within 1 day
-
🐛 Bug: `--clear-screen` not very useful (at least not for dealing with large test/error output)Openstatus: in triage type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
mochajs/mocha#6428 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
supabase/supabase-js#2768 ·
Maintainers usually reply within 1 day