Claude Code Env Vars Not Working
Why a Claude Code environment variable you set doesn't take effect: settings env vs shell export vs .env, and when each is read. Reproduced with dummy values.
“I set it, but it isn’t applied” is the most common Claude Code environment-variable problem. Usually the cause is where you set it versus when it is read. We checked each location against the official docs and reproduced the shell side locally. Secrets leaking into subprocesses are out of scope here; see Keep Secrets Away from Claude Code.
Setup
Linux test box, Claude Code 2.1.301, GNU bash 5.2.37, Node.js v20.19.2, jq 1.7, on 2026-10-16. Temp directory, shell started with env -i, dummy values only (DUMMY_*). We did not observe a live Claude Code session; Claude Code behavior comes from the docs.
Three places
| Where | Read when | After editing |
|---|---|---|
export in the launching shell | At claude startup | Next launch |
env in settings.json | Startup and on save | Applied to the running session; removals wait for next launch |
Project .env | No documented auto-loading | — |
Source: https://code.claude.com/docs/en/env-vars (checked 2026-10-16). Features that read variables once at startup, such as OpenTelemetry, keep startup values until relaunch.
Same variable in both
If a variable is in both your shell and a settings env block, the settings value usually wins (same source). Pairs such as ANTHROPIC_MODEL and the model key are decided per pair (https://code.claude.com/docs/en/settings, checked 2026-10-16). Most project env values apply only after you trust the folder (same source).
export in Bash doesn’t persist
An export in one Bash tool command isn’t there in the next (https://code.claude.com/docs/en/tools-reference, checked 2026-10-16):
$ bash -c 'export DUMMY_TIMEOUT_MS=1200000; echo "1st: ${DUMMY_TIMEOUT_MS:-unset}"'
1st: 1200000
$ bash -c 'echo "2nd: ${DUMMY_TIMEOUT_MS:-unset}"'
2nd: unset
To persist, point CLAUDE_ENV_FILE at a script before launch or fill it from a SessionStart hook (same source; see Claude Code Hooks in Practice).
Which startup file your export is in
The docs say to put exports in ~/.bashrc or similar, so the shell that launches claude has them (https://code.claude.com/docs/en/env-vars, checked 2026-10-16). Which file that shell reads is the trap:
bash -c profile=none bashrc=none
bash -lc profile=sk-dummy-0000-NOT-REAL bashrc=none
bash -ic profile=none bashrc=sk-dummy-0000-NOT-REAL
Login shells read .bash_profile, interactive shells .bashrc, and bash -c neither. Directories you add to PATH are missing too when the file you added them in isn’t read. For values that must not depend on how claude starts, use the settings env block, which the docs say applies however claude is launched.
.env isn’t loaded automatically
We found no statement in the env-var docs that Claude Code reads a project .env (checked 2026-10-16). Your app must load it; in Node, --env-file:
$ node -e 'console.log(process.env.DUMMY_API_KEY ?? "undefined")'
undefined
$ node --env-file=.env -e 'console.log(process.env.DUMMY_API_KEY)'
sk-dummy-0000-NOT-REAL
settings.json pitfalls
~and$HOMEaren’t expanded; use absolute paths (env-vars docs). Our JSON value stayed~/dummy-dir.- Settings files are strict JSON; trailing commas or
//comments become a Settings Error at next start (settings docs).jq .catches it first. - You can’t delete a variable from a settings file; override it with
"VAR": ""(env-vars docs).
Discarded errors hide missing config
source .envrc 2>/dev/null continues silently when the file is missing, leaving variables empty. For us, only some commands then failed with auth errors, which slowed diagnosis.
$ bash -c 'source ./.envrc 2>/dev/null; echo "exit=$? DUMMY_TOKEN=${DUMMY_TOKEN:-empty}"'
exit=1 DUMMY_TOKEN=empty
Keep errors visible and chain with &&.
Auto-loading with direnv
The external tool direnv switches environments from a prompt hook, so it doesn’t work as is in Claude Code’s Bash tool. The docs offer a CwdChanged hook, suggested for tools like direnv, which can write to CLAUDE_ENV_FILE (https://code.claude.com/docs/en/hooks, checked 2026-10-16). For the working-directory carry-over trap, see Choosing a Coding Agent.
Summary
- Shell exports are read at startup; relaunch after changing them.
envin settings applies on save, except removals and startup-only features.- Settings
envusually beats the shell; check variable/setting pairs individually. - Which rc file loads depends on how the shell starts.
.envisn’t auto-loaded,~isn’t expanded, trailing commas break JSON.