[BUG] CLAUDE_CODE_ATTRIBUTION_HEADER=0 also blocks auto classifier model
Status Fixed / completed
Reported on v2.1.159
Maintainer reply None cached
Activity 5 comments · opened Jun 1, 2026 · closed Aug 19, 2026
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?
When CLAUDE_CODE_ATTRIBUTION_HEADER=0 is set the auto classifier models fails with this message:
⎿ Error: claude-opus-4-8[1m] is temporarily unavailable, so auto mode cannot determine the safety of Write right now. Wait briefly and then try this action again. If it keeps failing, continue with other tasks that don't require this
action and come back to it later. Note: reading files, searching code, and other read-only operations do not require the classifier and can still be used.
(for any model, not just opus)
when unsetting the variable it (auto classifier) works fine reliably
(variable set because of other documenting caching issues with it - i.e. https://github.com/anthropics/claude-code/issues/24168 )
What Should Happen?
Classifier model should not fail, or if this is an intentional anthropic position error message should be clearer.
Error Messages/Logs
Steps to Reproduce
- set CLAUDE_CODE_ATTRIBUTION_HEADER=0
- fire up claude code
- try to write a file to /tmp
- get error
- exit
- unset the var
- fire up claude code
- try to write a file to /tmp
- it works
repro script:
#!/usr/bin/env bash
# Repro: does CLAUDE_CODE_ATTRIBUTION_HEADER=0 break the auto-mode safety classifier?
#
# Method
# ------
# For each model, run `claude -p` in AUTO permission mode and force ONE benign Bash
# tool call that writes a sentinel file. The tool is deliberately NOT allowlisted, so
# the auto-mode safety classifier is the gate that must approve it. Therefore:
# classifier OK -> benign action approved -> sentinel file created
# classifier broken -> "cannot determine the safety" -> blocked -> no sentinel
# Each model is run twice: control (header unset) vs test (header=0). A difference == repro.
#
# Oracle is the sentinel file (robust); the run log is kept for diagnosis and is also
# grepped for the classifier error string to distinguish a true block from any other
# failure.
#
# Models
# ------
# Default sweep: a stable Sonnet baseline, Opus 4.8, and Opus 4.8 with 1M context.
# * claude-sonnet-4-6 -> clean baseline (widely available; control should pass)
# * claude-opus-4-8 -> shows the header effect is not 1M-specific
# * claude-opus-4-8[1m] -> the variant from the original report
# NOTE: claude-opus-4-8[1m] is ALSO independently affected by transient Opus/1M-context
# capacity outages that emit the SAME error string. If its CONTROL run fails, that's the
# capacity bug, not the header — which is exactly why Sonnet is included as a clean control.
#
# Usage:
# ./repro-attribution-classifier.sh
# MODELS="claude-opus-4-8[1m]" ./repro-attribution-classifier.sh # just the 1M variant
# MODELS="claude-sonnet-4-6 claude-haiku-4-5" ./repro-... # custom sweep
set -uo pipefail
CLAUDE="$(command -v claude)"
[ -z "$CLAUDE" ] && { echo "claude not on PATH"; exit 3; }
read -r -a MODELS <<< "${MODELS:-claude-sonnet-4-6 claude-opus-4-8 claude-opus-4-8[1m]}"
LOGDIR="$(mktemp -d /tmp/cc-classifier-logs.XXXXXX)"
TO="$(command -v timeout || command -v gtimeout || true)"
run_to(){ if [ -n "$TO" ]; then "$TO" 120 "$@"; else "$@"; fi; }
slug(){ printf '%s' "$1" | LC_ALL=C tr -c 'A-Za-z0-9' '_'; }
# run_case <model> <label> <env-modifiers...> (env mod = `-u VAR` or `VAR=val`)
# sets globals: REPLY_CREATED (YES/NO), REPLY_BLOCKED (YES/no)
run_case(){
local model="$1" label="$2"; shift 2
local sentinel; sentinel="$(mktemp -u /tmp/cc-classifier-repro.XXXXXX)"
local prompt="Use the Bash tool to run this exact command and nothing else, then stop: echo OK > ${sentinel}"
local log="${LOGDIR}/$(slug "$model").${label}.log"
rm -f "$sentinel"
run_to env "$@" "$CLAUDE" -p "$prompt" \
--permission-mode auto --model "$model" --output-format json \
>"$log" 2>&1
local ec=$?
REPLY_CREATED="NO"; [ -f "$sentinel" ] && REPLY_CREATED="YES"
REPLY_BLOCKED="no"; grep -qi "cannot determine the safety" "$log" && REPLY_BLOCKED="YES"
printf ' %-8s exit=%-4s sentinel_created=%-4s classifier_blocked=%-4s log=%s\n' \
"$label" "$ec" "$REPLY_CREATED" "$REPLY_BLOCKED" "$log"
rm -f "$sentinel"
}
echo "claude : $CLAUDE ($($CLAUDE --version 2>/dev/null))"
echo "models : ${MODELS[*]}"
echo "logs : $LOGDIR"
echo
overall=0
for model in "${MODELS[@]}"; do
echo "=== model: $model ==="
echo " [1/2] CONTROL — CLAUDE_CODE_ATTRIBUTION_HEADER unset"
run_case "$model" control -u CLAUDE_CODE_ATTRIBUTION_HEADER
ctrl="$REPLY_CREATED"
echo " [2/2] TEST — CLAUDE_CODE_ATTRIBUTION_HEADER=0"
run_case "$model" test CLAUDE_CODE_ATTRIBUTION_HEADER=0
test_created="$REPLY_CREATED"; test_blocked="$REPLY_BLOCKED"
printf ' verdict: '
if [ "$ctrl" = "YES" ] && [ "$test_created" = "NO" ]; then
echo "REPRO CONFIRMED (control works; header=0 blocks; classifier_blocked=$test_blocked)"
elif [ "$ctrl" = "NO" ]; then
echo "INCONCLUSIVE — control did not execute (model unavailable / refused). See log."
overall=1
else
echo "NOT REPRODUCED — both runs executed the action."
fi
echo
done
echo "all logs: $LOGDIR"
exit "$overall"
Output
bennight@Chriss-MacBook-Air projects % ./repro-attribution-classifier.sh
claude : /Users/bennight/.local/bin/claude (2.1.159 (Claude Code))
models : claude-sonnet-4-6 claude-opus-4-8 claude-opus-4-8[1m]
logs : /tmp/cc-classifier-logs.c8u1e5
=== model: claude-sonnet-4-6 ===
[1/2] CONTROL — CLAUDE_CODE_ATTRIBUTION_HEADER unset
control exit=0 sentinel_created=YES classifier_blocked=no log=/tmp/cc-classifier-logs.c8u1e5/claude_sonnet_4_6.control.log
[2/2] TEST — CLAUDE_CODE_ATTRIBUTION_HEADER=0
test exit=0 sentinel_created=NO classifier_blocked=YES log=/tmp/cc-classifier-logs.c8u1e5/claude_sonnet_4_6.test.log
verdict: REPRO CONFIRMED (control works; header=0 blocks; classifier_blocked=YES)
=== model: claude-opus-4-8 ===
[1/2] CONTROL — CLAUDE_CODE_ATTRIBUTION_HEADER unset
control exit=0 sentinel_created=YES classifier_blocked=no log=/tmp/cc-classifier-logs.c8u1e5/claude_opus_4_8.control.log
[2/2] TEST — CLAUDE_CODE_ATTRIBUTION_HEADER=0
test exit=0 sentinel_created=NO classifier_blocked=YES log=/tmp/cc-classifier-logs.c8u1e5/claude_opus_4_8.test.log
verdict: REPRO CONFIRMED (control works; header=0 blocks; classifier_blocked=YES)
=== model: claude-opus-4-8[1m] ===
[1/2] CONTROL — CLAUDE_CODE_ATTRIBUTION_HEADER unset
control exit=0 sentinel_created=YES classifier_blocked=no log=/tmp/cc-classifier-logs.c8u1e5/claude_opus_4_8_1m_.control.log
[2/2] TEST — CLAUDE_CODE_ATTRIBUTION_HEADER=0
test exit=0 sentinel_created=NO classifier_blocked=YES log=/tmp/cc-classifier-logs.c8u1e5/claude_opus_4_8_1m_.test.log
verdict: REPRO CONFIRMED (control works; header=0 blocks; classifier_blocked=YES)
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.159
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
4 Comments
Having exact same issue. Note CLAUDE_CODE_ATTRIBUTION_HEADER is needed for caching to work on other providers.
Confirming this on Claude Code 2.1.187 (current), Max subscription, direct to
api.anthropic.com. Clean A/B — the only variable isCLAUDE_CODE_ATTRIBUTION_HEADER:| direct, 2.1.187 | header OFF (
=0) | header ON (default) ||---|---|---|
|
claude-opus-4-8| 429rate_limit_error| 200 ||
claude-sonnet-4-6| 429rate_limit_error| 200 |The main
/v1/messageschat is unaffected — only the auto-mode classifier sub-request 429s. And it isn't usage-driven: it reproduces instantly and deterministically on a single gray-zone classification (anln -sfsymlink) and clears the instant the header is re-enabled. Still broken on 2.1.187, so no recent release fixes it.Repro (~30s):
(I'd originally mis-filed this as an account-side classifier-pool problem in #60438; it isn't — this is the real root cause. Closed mine in favor of this one.)
can we get this fixed?
Another way to hit this bug, and a much harder one to spot: the background-session daemon keeps the environment of whichever session first started it. Mine was started weeks ago from a shell that had
CLAUDE_CODE_ATTRIBUTION_HEADER=0, so every background session since then ran with the variable set, while no shell or config file still had it. Foreground sessions worked; background sessions silently didn't.Claude Code 2.1.201, Linux, claude.ai Max (OAuth), model claude-opus-4-8.
Effect: for 12+ days, auto mode in every background session denied every Bash call with "claude-opus-4-8 is temporarily unavailable, so auto mode cannot determine the safety of Bash right now...". The same sessions could still reply normally, and foreground sessions running at the same moment got normal approvals. The variable existed only inside the daemon:
Claude Code's own error dumps (
/tmp/claude-$UID/auto-mode-classifier-errors/) show the same error as this issue in 11 background sessions, Jun 23 - Jul 4, all identical:To confirm the variable (not backgrounding itself) is the cause, I ran the same prompt with
claude -p, auto mode, opus, changing only the variable:| run | result |
|---|---|
| foreground, variable unset (x3) | approved, command ran |
| foreground, variable set to 0 | denied, 429 (
req_011CciWRUTenp76xhAsB2NEQ) || background via the daemon (x3) | denied, 429, retries forever |
Worth noting on this issue because it is near-impossible to diagnose: the variable is invisible (it only shows up in
/proc/<daemon>/environ), the error message hides the 429 ("temporarily unavailable", which the docs describe as server overload, so it reads as an outage; some reports in #74248 / #74351 / #74280 / #69950 may actually be this), and background sessions never recover until the daemon is killed and restarted from a clean shell.Suggestions: include the HTTP status in the error message, and don't let background sessions inherit a weeks-old daemon environment (or at least surface it in
claude doctor).<sup>*ps: edited: got claude to rewrite this comment and make it less jargony (y) </sup>