openai / openai/codex-plugin-cc

SessionStart hook appends duplicate exports to CLAUDE_ENV_FILE until every Bash call fails

Open Beginner friendly
#661 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.