Tutorials
Ren Okabe10 min read30 views

CLAUDE_CODE_TMPDIR is ignored in project settings (2026)

Claude Code v2.1.251 stopped project settings setting CLAUDE_CODE_TMPDIR, CLAUDE_CONFIG_DIR, HOME, TMPDIR and the XDG family from a .claude/settings.json env block. Nothing errors. Here is the full ignore list, the four scopes it splits into, and the debug flag that shows you the drop.

A glowing electric blue horizontal boundary line on a near-black background, with three dimmed hollow tokens stopped above it and solid blue tokens passing through below
A glowing electric blue horizontal boundary line on a near-black background, with three dimmed hollow tokens stopped above it and solid blue tokens passing through below
On this page

Quick Answer (September 2026). CLAUDE_CODE_TMPDIR set in a project's .claude/settings.json env block does nothing. As of Claude Code v2.1.251 the value is dropped, and the same applies to CLAUDE_CONFIG_DIR, HOME, TMPDIR, TMP, TEMP and the XDG_* family. Set them in your shell, your user settings, or managed settings instead. Nothing errors and nothing is printed at normal verbosity, so the usual symptom is a temp or config path that silently stays at its default. Run claude --debug and Claude Code will tell you which variables it threw away.

Anthropic Everything below is sourced from Anthropic's own settings reference and environment variables reference, the anthropics/claude-code changelog, and npm registry publish timestamps read directly. Version dates are pinned to published artifacts rather than to release-blog datelines.

What actually changed

One changelog line carries it.

Claude Code 2.1.251, verbatim: "Changed project-level .claude/settings.json env to no longer set CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, or TMPDIR/TMP/TEMP; set them in your shell, user, or managed settings instead"

That release was published to npm at 2026-08-28T15:34:26.421Z, so at the time of writing the behaviour is about nine days old.

The reasoning is not stated in the changelog but it is stated in the docs, and it is worth reading before you go looking for a workaround.

Settings reference, verbatim: "Project and local settings can't set variables that a checked-out repository shouldn't control; set those in your shell, user settings, or managed settings instead. Claude Code drops each one and logs a warning you can see with claude --debug."

That is the whole design in one sentence. A project settings file is a file that arrives with a git clone. Anything in it is chosen by whoever wrote the repository, not by you. CLAUDE_CONFIG_DIR decides where your settings, session history, plugins and credentials live. A repository being able to move that directory is a repository being able to decide where your credentials are read from and written to. So the fix is a scope restriction rather than a feature removal: the variables still work, they just stop being settable from the one file an untrusted third party writes.

The changelog names three things. The real list is much longer.

This is the part that will cost you time if you work from the changelog alone. The 2.1.251 entry names CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, and the TMPDIR/TMP/TEMP trio. The settings reference lists three whole categories, and the trio is only part of the first one.

Settings reference, verbatim: "Variables that choose where Claude Code stores or writes its own files: CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, and the operating-system directory variables such as HOME, TMPDIR, TMP, TEMP, and the XDG_* family."

Settings reference, verbatim: "Variables that export session content: OTEL_LOG_RAW_API_BODIES and the detailed beta tracing pair ENABLE_BETA_TRACING_DETAILED and BETA_TRACING_ENDPOINT."

Settings reference, verbatim: "Variables that change how Claude Code starts or syncs, such as CLAUDE_CODE_PROCESS_WRAPPER, CLAUDE_CODE_SYNC_SKILLS, CLAUDE_CODE_SYNC_PLUGINS, CLAUDE_CODE_PLUGIN_CACHE_DIR, and CLAUDE_CODE_PLUGIN_SEED_DIR."

HOME and the XDG_* family never appear in the changelog line at all. Neither does the tracing group, and that group is arguably the sharper half of the change: OTEL_LOG_RAW_API_BODIES emits your Messages API request and response bodies as log events, and the docs note that those bodies include the entire conversation history. A repository that could set that key from its own settings file could turn on full transcript export.

The changelog does cover it, but in a different bullet, in a different section of the same release, with no cross-reference to the one everybody quotes.

Claude Code 2.1.251, verbatim: "Fixed project settings being able to enable detailed beta tracing or raw API body logging, and a lower-scope beta tracing endpoint bypassing an OTLP collector pinned by managed settings or a host app"

So one coherent hardening is split across a "Changed" line and a "Fixed" line. Read either on its own and you will conclude something narrower than what shipped. The docs are the artifact that unifies them.

There are four tiers, not one

"Ignored" is not a single behaviour here. The settings reference describes four distinct scopes, and confusing them is how people end up moving a variable to the wrong file.

Tier 1: ignored in project and local settings only. Everything in the three categories above. These work perfectly well from your shell, your user settings file, or managed settings. This is the tier the 2.1.251 change created.

Tier 2: ignored from every settings file, because the hosting environment owns them.

Settings reference, verbatim: "Identity variables that Claude Code's hosting environments own, such as CLAUDE_CODE_REMOTE and CLAUDE_CODE_ACCOUNT_UUID, are ignored from every file."

Tier 3: ignored from every settings file, because Claude Code exports them itself. CLAUDE_CODE_MESSAGING_SOCKET and CLAUDE_CODE_MESSAGING_TOKEN. The docs pin these individually: ignoring the socket variable needs v2.1.224 or later, and ignoring the token needs v2.1.228 or later.

Tier 4: read from the launch environment only. CLAUDE_CODE_PROJECT_DIR_NAME (v2.1.234 or later) and CLAUDE_CODE_RESTRICTED. These are stricter than tier 1 in a way that catches people out: moving them to your user settings file does not help either, because no settings file is consulted for them at all.

Environment variables reference, verbatim, on CLAUDE_CODE_PROJECT_DIR_NAME: "Claude Code ignores this variable when CLAUDE_CONFIG_DIR is unset, and reads it only from the environment you start claude from, never from a settings file env block."

That last one is the practical trap. The obvious response to "project settings cannot set this" is "fine, I will put it in my user settings", and for tier 1 that is exactly right. For tier 4 it fails just as silently as the project file did.

How to see it happen

You do not have to guess which tier you are in, and this is the single most useful line in the whole change. The drop is logged.

bash
claude --debug

The settings reference states that Claude Code drops each ignored variable and logs a warning visible with claude --debug. At normal verbosity there is no message, which is why the failure reads as "my setting did nothing" rather than as an error. If you are debugging a path that will not move, start here before you start editing files.

A second confirmation, from the other direction: /status and claude doctor were both given an "Organization policy" line in v2.1.261 that explains why managed settings could not be loaded. If your variable is supposed to arrive from managed settings and is not arriving at all, that is a different failure from the one described in this article, and those two commands separate the cases.

Where to put it instead

For the tier 1 variables, all three of these work. Pick by how far you want the value to travel.

Your shell, for one machine and one person. This is what Anthropic's own example does:

bash
alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'

Your user settings file, at ~/.claude/settings.json, for every project you open on that machine:

json
{
  "env": {
    "CLAUDE_CODE_TMPDIR": "/var/tmp/cc",
    "CLAUDE_CONFIG_DIR": "/Users/you/.claude-work"
  }
}

Managed settings, for a fleet, where an administrator sets the value and a repository cannot contradict it.

What does not work, and is the thing to delete if you find it, is the same block inside .claude/settings.json or .claude/settings.local.json in a repository. Note that the local file is covered too. "Local" sounds personal, and settings.local.json is normally git-ignored and genuinely is yours, but for this purpose Claude Code classifies it with project settings rather than with user settings.

The precedence rule most people have backwards

While you are moving the variable, there is a related rule worth getting right, because the folk version of it is the reverse of the documented one.

Settings reference, verbatim: "A value here overwrites the same variable exported in your shell, and when more than one settings file sets a variable, the highest-precedence one applies. To cancel a shell export, set the variable to ""."

A settings env value beats your shell export, not the other way round. So if you have moved CLAUDE_CODE_TMPDIR into your user settings and it still is not taking effect, a stale shell export is not the explanation; something higher-precedence is setting it, or you are in one of the tiers above.

The cancel form is worth memorising because there is no other way to express it. An empty string means "unset", and it has two documented consequences: Claude Code treats an empty value as unset for provider selection, and subprocesses inherit the empty value rather than the shell's original.

Two more details from the same section that only bite in specific cases. NO_COLOR and FORCE_COLOR set in an env block reach only subprocesses, so to change Claude Code's own interface colours you have to set them in your shell before launching. And values in an env block are plain text in the settings file and reach every subprocess Claude Code starts, which is why the docs point at otelHeadersHelper for a rotating OTLP bearer token and apiKeyHelper for API credentials rather than at env.

When env values are applied, which is not always at startup

The other reason a variable can appear inert is timing. The settings reference splits this by file, and project settings are the odd one out.

Settings reference, verbatim: "From user settings, --settings, and managed settings: at startup, and again in the running session when a saved change alters the merged env."

Settings reference, verbatim: "From project and local settings: after you trust the workspace, or at startup in -p mode, which never shows the trust dialog, and again when a saved change alters the merged env."

Three things fall out of that pair.

First, project env values are gated on the workspace trust dialog. In a fresh clone that you have not trusted yet, a project env block has not been applied at all, which produces the same "my setting did nothing" symptom for an entirely different reason.

Second, -p mode never shows the trust dialog, so project env values apply at startup there. That asymmetry matters for CI and for scripted runs: the non-interactive path is the one where a repository's env block takes effect with no prompt, which is a reasonable extra motivation for the tier 1 restriction.

Third, env is not startup-only in any of these files. A saved change to a settings file re-applies during the running session. And since v2.1.246 there is a fourth case:

Settings reference, verbatim: "After you move the session with /cd on v2.1.246 or later: the new directory's project and local env values, on top of the previous directory's."

On top of, rather than instead of. If you /cd from one repository to another, the second repository's project env values stack over the first one's, and a variable set only by the first directory is still set. That is a small thing until you are chasing a value you cannot find a source for.

The reference page warns on far fewer variables than the ignore list names

If you look a variable up the natural way, by finding its entry in the environment variables reference, you will only be warned about some of them. This is measurable rather than impressionistic. On that page today the exact phrase "Ignored in project and local settings" appears four times, on CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, BETA_TRACING_ENDPOINT and OTEL_LOG_RAW_API_BODIES.

Three more variables carry a warning in different words. ENABLE_BETA_TRACING_DETAILED says "Both variables are ignored in project and local settings", CLAUDE_CODE_RESTRICTED says Claude Code ignores it in a settings file's env block and needs v2.1.248 or later, and CLAUDE_CODE_PROJECT_DIR_NAME uses the "never from a settings file env block" phrasing quoted earlier.

So seven entries are marked, in four different wordings, against a settings-reference list that names sixteen individual variables plus the operating-system directory variables and the whole XDG_* family. CLAUDE_CODE_PROCESS_WRAPPER, CLAUDE_CODE_SYNC_SKILLS, CLAUDE_CODE_PLUGIN_CACHE_DIR, CLAUDE_CODE_PLUGIN_SEED_DIR, CLAUDE_CODE_MESSAGING_SOCKET, CLAUDE_CODE_MESSAGING_TOKEN and CLAUDE_CODE_REMOTE are all named in the ignore list and carry no warning in their own entries, and CLAUDE_CODE_SYNC_PLUGINS and CLAUDE_CODE_ACCOUNT_UUID do not appear on the environment variables page at all.

Neither page is wrong. They are simply two different documents with two different jobs, and the per-variable one is the one you will reach for. The list in the settings reference is the authoritative version, so read that section once rather than trusting the absence of a warning on an individual entry.

The stable release channel still has the old behaviour

npm One last thing that is easy to miss and changes who this applies to. The published npm dist-tags at the time of writing are stable: 2.1.236 and latest: 2.1.263. Version 2.1.236 was published at 2026-08-19T18:45:14.539Z, which is before 2.1.251.

So the two channels currently disagree about this. On latest, a repository cannot set your config directory from its own settings file. On stable, it still can, because the version stable points at predates the restriction. If you maintain a policy that assumes the new behaviour, the version people are actually running is the thing to check.

Stated as a fact about today's dist-tags. This is not a claim about what stable pointed at during any earlier window, which I did not read.

What this change does not do

Worth bounding, because two adjacent things are easy to conflate with it.

It does not stop a repository setting other variables. The env key's scope is still "any file". Model selection, timeouts and limits, feature toggles and telemetry settings are described as safe and still apply at startup from every settings file. The restriction is specific to the categories listed above.

It is also not the same control as --restricted, which arrived three releases earlier and is much broader.

Claude Code 2.1.248, verbatim: "Added --restricted (or CLAUDE_CODE_RESTRICTED=1): removes the built-in tools that run commands or code and WebFetch (unless named in --tools), keeps file tools inside the working directory, refuses bypassPermissions, and ignores user, project and local settings files"

Note the last clause. Restricted mode ignores the user settings file too, not just the project one. If you are relying on a user-level env value and you also run restricted sessions, that value is not present in them.

The honest bound on this article

I did not run a Claude Code session to reproduce the drop key by key. Everything above is the vendor's documented and changelogged contract plus publish timestamps read directly from the npm registry, and the counts of warning phrases are from the live documentation pages as they stand today. The claude --debug warning is the mechanism the docs name for observing the behaviour yourself, and it is the first thing to run if your own results disagree with any of this.

If you want the wider context for the file this lands in, the settings scope model covers how project settings can and cannot constrain a session, and where Claude Code keeps memory and transcripts covers what CLAUDE_CONFIG_DIR actually moves when you set it in a place that works.

GitHub Primary sources: the Anthropic settings reference and environment variables reference, the anthropics/claude-code changelog, and the @anthropic-ai/claude-code package on npm for publish timestamps.

R

Written by

Ren Okabe

Ren builds and breaks agent tooling, then writes down the parts the documentation assumes you already know.

Frequently asked questions

Why does CLAUDE_CODE_TMPDIR do nothing in my .claude/settings.json?

As of Claude Code v2.1.251, project-level and local settings files can no longer set it. The changelog entry reads: 'Changed project-level .claude/settings.json env to no longer set CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, or TMPDIR/TMP/TEMP; set them in your shell, user, or managed settings instead.' Claude Code drops the value silently at normal verbosity. The settings reference explains the rule as: project and local settings cannot set variables that a checked-out repository should not control.

How do I confirm Claude Code dropped my environment variable?

Run 'claude --debug'. Anthropic's settings reference states that Claude Code drops each ignored variable and logs a warning you can see with that flag. At normal verbosity nothing is printed, which is why the symptom looks like a setting that simply did nothing rather than an error.

Where should I set CLAUDE_CODE_TMPDIR and CLAUDE_CONFIG_DIR instead?

In your shell, in your user settings file at ~/.claude/settings.json, or in managed settings. All three still work. Anthropic's own example for running multiple accounts is a shell alias: alias claude-work='CLAUDE_CONFIG_DIR=~/.claude-work claude'. What no longer works is an env block in a repository's .claude/settings.json or .claude/settings.local.json.

Does moving the variable to my user settings always fix it?

For most of the ignored variables yes, but not for all of them. CLAUDE_CODE_PROJECT_DIR_NAME and CLAUDE_CODE_RESTRICTED are read from the launch environment only and are ignored from every settings file, including your user file. CLAUDE_CODE_REMOTE, CLAUDE_CODE_ACCOUNT_UUID, CLAUDE_CODE_MESSAGING_SOCKET and CLAUDE_CODE_MESSAGING_TOKEN are also ignored from every file. Those need to be set in the environment you start claude from, or not at all.

Which variables besides CLAUDE_CODE_TMPDIR are ignored in project settings?

The settings reference lists three categories. Storage locations: CLAUDE_CONFIG_DIR, CLAUDE_CODE_TMPDIR, HOME, TMPDIR, TMP, TEMP and the XDG_* family. Session content export: OTEL_LOG_RAW_API_BODIES, ENABLE_BETA_TRACING_DETAILED and BETA_TRACING_ENDPOINT. Startup and sync: CLAUDE_CODE_PROCESS_WRAPPER, CLAUDE_CODE_SYNC_SKILLS, CLAUDE_CODE_SYNC_PLUGINS, CLAUDE_CODE_PLUGIN_CACHE_DIR and CLAUDE_CODE_PLUGIN_SEED_DIR. The changelog line names only the first three of these, so the list is wider than it appears.

Does a settings env value override an environment variable exported in my shell?

Yes. The settings reference states that a value in an env block overwrites the same variable exported in your shell, and that when more than one settings file sets a variable the highest-precedence one applies. To cancel a shell export you set the variable to an empty string, which Claude Code treats as unset for provider selection while subprocesses inherit the empty value.

Is this change in the stable release channel?

Not at the time of writing. The published npm dist-tags read stable 2.1.236 and latest 2.1.263, and 2.1.236 was published on 19 August 2026, before 2.1.251 shipped on 28 August 2026. So a session on the stable channel still allows a repository's project settings to set these variables. This is a statement about today's dist-tags rather than about any earlier window.

Tutorials

keybindingFlavor no longer has any effect in Claude Code (2026)

keybindingFlavor is deprecated and inert in Claude Code v2.1.261 and later. The readline word-editing behaviour it gated is now on for everyone. Ctrl+W deletes back to whitespace while the Alt keys stop at punctuation, and neither can be rebound: the keybindings reference documents 115 actions and none of them is a word-editing action.

10 min read23