CLAUDE_ENV_FILE written but not sourced for Bash tool calls on Windows

Status Fixed / completed
Reported on v2.1.33
Maintainer reply ✓ Yes — claude[bot]
Activity 7 comments · opened Feb 23, 2026 · closed Apr 17, 2026
💡 Likely answer: A maintainer (claude[bot], contributor) responded on this thread — see the highlighted reply below.

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Windows, CLAUDE_ENV_FILE is correctly provided to SessionStart hooks and the env file is written with valid export statements. However, subsequent Bash tool calls do not have access to the exported environment variables — the file is never sourced.

This differs from #15840 (env file not provided) and #14433 (not sourced after /clear). Here, the file IS provided and written on initial session start, but never sourced for any Bash tool call.

Reproduction Steps

  1. Create a SessionStart hook that writes to CLAUDE_ENV_FILE:
{
  "hooks": {
    "SessionStart": [{
      "hooks": [{
        "type": "command",
        "command": "node -e \"const fs=require('fs'); if(process.env.CLAUDE_ENV_FILE) fs.appendFileSync(process.env.CLAUDE_ENV_FILE, 'export MY_TEST_VAR=hello\n');\""
      }]
    }]
  }
}
  1. Start a new Claude Code session on Windows
  2. Ask Claude to run: echo $MY_TEST_VAR
  3. Result: empty. The variable is not set.

Diagnostic Evidence

Added debug logging to the SessionStart hook to write process.env.CLAUDE_ENV_FILE to a file:

CLAUDE_ENV_FILE=C:\Users\<user>\.claude\session-env\<session-id>\sessionstart-hook-1.sh

Contents of sessionstart-hook-1.sh (correctly written):

export WIZ_SCRIPTS="C:/Users/<user>/.claude/plugins/cache/lansweeper-tools/wiz/3.7.0/scripts"
export TMPDIR="/tmp/claude"
export CLAUDE_CODE_DISABLE_1M_CONTEXT=1

Bash tool call results:

WIZ_SCRIPTS=        (empty)
TMPDIR=/tmp          (should be /tmp/claude)
CLAUDE_ENV_FILE=     (empty)

None of the exported variables are available. The file exists, has correct content, but is not sourced.

Expected Behavior

Per the hooks documentation: "Claude Code will source this file before each Bash command, making the environment persistent across all commands."

Environment variables written to CLAUDE_ENV_FILE during SessionStart should be available in subsequent Bash tool calls on Windows.

Environment

  • Claude Code version: 2.1.33
  • OS: Windows 11 Enterprise 10.0.26200
  • Shell: Git Bash (via Claude Code's default Windows shell)
  • Node.js: v22.20.0

Related Issues

  • #15840 — CLAUDE_ENV_FILE not provided to hooks (macOS) — different: on Windows it IS provided
  • #14433 — not sourced after /clear (macOS) — different: on Windows it's not sourced at all, not just after /clear
  • #24775 — session ID mismatch on resume — potentially related if the sourcing logic has a path resolution issue on Windows

View original on GitHub ↗

7 Comments

github-actions[bot] · 6 months ago

Found 2 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/26610
  2. https://github.com/anthropics/claude-code/issues/27161

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ls-edwin-bekaert · 6 months ago

Additional evidence: debug log shows no env file sourcing

Ran CC with debug logging on Windows. The SessionStart hook runs and writes to CLAUDE_ENV_FILE successfully, but there is no debug log entry showing CC sourcing the resulting .sh file.

Relevant excerpts from the debug log:

2026-02-23T20:37:14.239Z [DEBUG] Hooks: Config-based async hook, backgrounding process async_hook_47624
2026-02-23T20:37:14.241Z [DEBUG] Hooks: Registering async hook async_hook_47624 (SessionStart:startup) with timeout 10000ms
...
2026-02-23T20:37:14.654Z [DEBUG] Hooks: Checking initial response for async: **Today's date:** ...
2026-02-23T20:37:14.666Z [DEBUG] Hook SessionStart:startup (SessionStart) success: ...

The hook completes successfully (writes export WIZ_SCRIPTS=... to the env file), but there is no subsequent log line about sourcing the env file. On macOS/Linux there should be a sourcing step between hook completion and the first Bash tool call — on Windows this step appears to be missing entirely.

Subsequent Bash tool calls confirm none of the exported variables are available:

WIZ_SCRIPTS=        (empty — should be C:/Users/<user>/.claude/plugins/cache/...)
TMPDIR=/tmp          (should be /tmp/claude per the env file)
CLAUDE_ENV_FILE=     (empty)
sstraus · 5 months ago

Impact: plugin ecosystem broken on Windows

This bug completely breaks the plugin backtick command (!) mechanism on Windows. Any plugin that uses CLAUDE_ENV_FILE to export path variables for dynamic context in command/skill .md files is dead on arrival.

Concrete example: Our marketplace plugin (wiz) exports WIZ_SCRIPTS pointing to the plugin's scripts directory. Commands like /wiz:review use backtick commands:

!`node "$WIZ_SCRIPTS/team-extensions.js" review`

On macOS/Linux this works because CLAUDE_ENV_FILE is sourced. On Windows:

  • The env file is written correctly ✅
  • It is never sourced ❌
  • $WIZ_SCRIPTS is empty or stale in backtick commands ❌
  • Every ! block that references the variable crashes with MODULE_NOT_FOUND

Workaround attempted: We set WIZ_SCRIPTS as a persistent Windows user env var via PowerShell [Environment]::SetEnvironmentVariable(...). This only works after a full CC restart AND a new shell inheriting the updated env — it doesn't help the current session, and breaks on version upgrades (old path persists until restart).

Scale: This affects every backtick command across 26 commands and 18 skills in our plugin. There is no clean workaround — the plugin marketplace on Windows is fundamentally broken without env file sourcing.

Would love to see this prioritized. Happy to help test a fix.

bsekiewicz · 5 months ago

CLAUDE_ENV_FILE not sourced on Windows — root cause analysis

---

Root cause identified: This is not a sourcing bug - it's an intentional skip. The session env loader function (cn7 in the compiled bundle) contains an early return:

if (E1() === "windows") return V("Session environment not yet supported on Windows"), null;

The entire CLAUDE_ENV_FILE mechanism is disabled on Windows by design. The hook runs, the file gets created with valid content, but CC never attempts to source it.

Why the guard is unnecessary for Git Bash users:

CC on Windows defaults to Git Bash (CLAUDE_CODE_GIT_BASH_PATH is set in env). Git Bash is a POSIX shell - source $file works identically to Linux/Mac. The env files are already written with valid bash syntax (KEY=VALUE). There's nothing Windows-specific about sourcing them in Git Bash.

I verified this manually: source ~/.claude/session-env/<session-id>/sessionstart-hook-0.sh in a CC Bash tool call correctly loads all variables. The file format is fine, the shell can handle it - CC just refuses to try.

Proposed fix (minimal):

For Git Bash (the default shell on Windows), remove the early return - the existing Linux/Mac sourcing logic should work as-is since it's the same shell. If CC detects PowerShell or cmd.exe as the shell, the guard can remain until a per-shell adapter is implemented.

// Instead of blanket Windows skip:
if (E1() === "windows" && !isGitBash()) return null;

This would unblock the entire Windows plugin ecosystem without risking PowerShell/cmd edge cases.

claude[bot] contributor · 4 months ago

This issue was fixed as of version 2.1.111.

whizzzkid · 4 months ago

@claude this is still broken in v2.1.117, I fixed it by adding a custom global hook.

  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c --arg prefix \"source $HOME/.claude/profile.sh >/dev/null 2>&1; \" '{hookSpecificOutput: {hookEventName: \"PreToolUse\", updatedInput: (.tool_input | .command = ($prefix + .command))}}'"
          }
        ]
      }
    ]
  }
github-actions[bot] · 4 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.