Issue 00 · Autumn 2026日本語 · RSS
AgentKoji

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

WhereRead whenAfter editing
export in the launching shellAt claude startupNext launch
env in settings.jsonStartup and on saveApplied to the running session; removals wait for next launch
Project .envNo 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

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