9月8日に出た版の差分を読んでいて、ひとつの項目で手が止まりました。ゲートウェイやプロキシを挟んで Claude Code を動かしている構成が、直前の版で全リクエスト失敗していた、という内容です。
失敗したときのメッセージは Not signed in to the Cloud gateway。設定を何も変えていないのに、ある朝から一行も通らなくなる類のものです。
心当たりのある方は、いま settings.json を開いているかもしれません。そのファイルは、おそらく正しいままです。
結論から — 2.1.265 の回帰で、2.1.266 で戻っています
原因は CLAUDE_CODE_USE_GATEWAY という、公式ドキュメントに載っていない環境変数の扱いが変わったことでした。
2.1.265 より前は、この変数は ANTHROPIC_BASE_URL と ANTHROPIC_AUTH_TOKEN の両方が置かれている場合にだけ意味を持ち、単独では無視されておりました。ところが 2.1.265 では、単独で置かれているだけで Cloud ゲートウェイへのサインインを強制するようになりました。
その結果、API キーや apiKeyHelper、独自の認証ヘッダと組み合わせていた構成が、リクエストのたびに Not signed in to the Cloud gateway で落ちるようになったわけです。
9月8日公開の 2.1.266 で、単独では再び無視される元の挙動に戻りました。設定を直す必要はなく、版を上げれば済みます。 一次情報は claude-code v2.1.266 のリリースノート にあります。
| 版 | CLAUDE_CODE_USE_GATEWAY を単独で置いたとき |
|---|---|
| 2.1.264 以前 | 無視される(ANTHROPIC_BASE_URL と ANTHROPIC_AUTH_TOKEN が揃ったときだけ効く) |
| 2.1.265 | 単独でゲートウェイのサインインを強制する(既存構成が全リクエスト失敗) |
| 2.1.266 以降 | 無視される(元の挙動に復帰) |
ここで終われば短い連絡事項です。私がこの項目で手を止めたのは、直し方ではなく、壊れたときに人が最初に触る場所のほうでした。
設定を疑ってしまうのは、入力が三方向から集まるからです
Claude Code の認証は、環境変数・settings.json・apiKeyHelper の三方向から同じ一点へ入ってきます。しかも .claude/settings.json はユーザー階層とプロジェクト階層に分かれ、シェルのプロファイルは端末ごとに違います。
つまり「自分が置いた覚えのない値」が、そこそこの確率で効いています。社内のゲートウェイ導入手順書がシェルの共通プロファイルに一行足していた、以前の検証で export したまま消し忘れていた、といった具合です。今回のように文書化されていない変数であれば、なおさら記憶に残っておりません。
最初のうち、私はこういう場面で settings.json を書き換えて試すところから入っておりました。結果は芳しくありませんでした。動かない理由を探しているつもりで、正しかった設定のほうを崩していたのです。
壊れたときに疑うのは設定そのものではなく、設定と版の組み合わせです。 どちらか一方だけを見ても、変わっていない側を延々と直すことになります。
認証の入力面を一枚に並べるスクリプト
そこで手元に置いているのが、20 行ほどの小さなスクリプトです。値そのものではなく、何が置かれているかを一枚に並べます。
#!/usr/bin/env bash
# authsurface.sh — Claude Code の認証入力面を一枚に並べる
set -u
mask() {
local v="${1:-}"
[ -z "$v" ] && { echo "(unset)"; return; }
local n=${#v}
if [ "$n" -le 8 ]; then echo "set(len=$n)"; else echo "set(len=$n, tail=${v: -4})"; fi
}
# 版は必ず先頭に置く(あとで差分を取るときの軸になります)
echo "version=$( { claude --version 2>/dev/null || echo 'claude-not-found'; } | head -1 )"
# 値を出してはいけないもの=存在と長さだけ
for k in ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN; do
echo "$k=$(mask "${!k:-}")"
done
# 値を見ないと判断できないもの=そのまま出す
for k in ANTHROPIC_BASE_URL ANTHROPIC_CUSTOM_HEADERS CLAUDE_CODE_USE_GATEWAY \
CLAUDE_CODE_USE_BEDROCK CLAUDE_CODE_USE_VERTEX; do
echo "$k=${!k:-(unset)}"
done
# settings.json は「あるか」ではなく「何が書かれているか」を見る
for f in "$HOME/.claude/settings.json" "./.claude/settings.json" "./.claude/settings.local.json"; do
if [ -f "$f" ]; then
helper=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1])).get('apiKeyHelper','(none)'))" "$f" 2>/dev/null || echo "(parse-error)")
force=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1])).get('forceLoginMethod','(none)'))" "$f" 2>/dev/null || echo "(parse-error)")
echo "settings:$f apiKeyHelper=$helper forceLoginMethod=$force"
else
echo "settings:$f (absent)"
fi
doneゲートウェイを挟んだ端末で走らせると、こういう出力になります。
version=2.1.260 (Claude Code)
ANTHROPIC_API_KEY=set(len=23, tail=1234)
ANTHROPIC_AUTH_TOKEN=(unset)
ANTHROPIC_BASE_URL=https://llm-gw.internal.example/v1
ANTHROPIC_CUSTOM_HEADERS=(unset)
CLAUDE_CODE_USE_GATEWAY=1
CLAUDE_CODE_USE_BEDROCK=(unset)
CLAUDE_CODE_USE_VERTEX=(unset)
settings:/home/you/.claude/settings.json (absent)
settings:./.claude/settings.json (absent)
settings:./.claude/settings.local.json (absent)この一枚が手元にあれば、今回の回帰は見た瞬間に分かります。CLAUDE_CODE_USE_GATEWAY=1 が置かれていて、ANTHROPIC_AUTH_TOKEN が (unset) である——2.1.264 までは無視されていた組み合わせが、そこに残っているからです。
秘密の扱いだけ、ひと言添えておきます。mask を通しているのは、この出力を issue や社内チャットに貼れるようにするためです。長さと末尾4文字があれば「別のキーに入れ替わっていないか」は判断できますし、それ以上は要りません。環境変数そのものの一覧は Claude Code 環境変数 完全リファレンス のほうが網羅しております。
版と設定を組で残すと、切り分けが数秒で終わります
出力を日付つきで保存して、前回と比べるようにしています。走らせるのは、版を上げた直後です。
#!/usr/bin/env bash
# authsnap.sh — 認証入力面を保存し、直前の記録と比べる
set -u
DIR="${AUTHSNAP_DIR:-$HOME/.claude-authsnap}"
mkdir -p "$DIR"
NOW="$DIR/$(date +%Y%m%d-%H%M%S).txt"
"$(dirname "$0")/authsurface.sh" > "$NOW"
PREV=$(ls -1 "$DIR"/*.txt 2>/dev/null | grep -v "$(basename "$NOW")" | tail -1)
[ -z "${PREV:-}" ] && { echo "(比較対象なし。次回から差分が出ます)"; exit 0; }
if diff -u "$PREV" "$NOW" > /tmp/authsnap.diff; then
echo "RESULT: 入力面に変化なし"
else
CHANGED=$(grep -cE '^[+-][A-Za-z]' /tmp/authsnap.diff)
VERONLY=$(grep -E '^[+-][A-Za-z]' /tmp/authsnap.diff | grep -cv '^[+-]version=')
if [ "$VERONLY" -eq 0 ]; then
echo "RESULT: 変わったのは version だけです(設定は同じ)"
else
echo "RESULT: 設定側にも差分があります(${CHANGED} 行)"
fi
sed -n '4,40p' /tmp/authsnap.diff
fi手元で二回走らせて、あいだで版だけを上げた場合の出力がこれです。
compare: 20260910-150742.txt -> 20260910-150744.txt
RESULT: 変わったのは version だけです(設定は同じ)
-version=2.1.260 (Claude Code)
+version=2.1.265 (Claude Code)
ANTHROPIC_API_KEY=set(len=23, tail=1234)
ANTHROPIC_AUTH_TOKEN=(unset)
ANTHROPIC_BASE_URL=https://llm-gw.internal.example/v1一行だけの差分ですが、この一行が「自分は何も変えていない」を証拠にしてくれます。逆に ANTHROPIC_BASE_URL の側も動いていれば、判定は 設定側にも差分があります に変わり、疑う先が入れ替わります。
判定を二値にしているのには理由があります。ログを読むときの人間は、差分の中身より先に「自分のせいかどうか」を知りたがるからです。そこを先に片づけてしまえば、残りは落ち着いて読めます。
version を出力の先頭に固定しているのも同じ理屈です。diff は行の順に出ますので、先頭に置いておけば、版の差分が必ず最初の一行に現れます。
版が原因でなかったときに見る三か所
差分が 設定側にも差分があります になった場合、私はこの順で見ております。
forceLoginMethodが管理設定で固定されていないか。"gateway"が入っていると、残っている API キーや claude.ai のログインは無視され、/loginが必須になります。Bedrock・Vertex AI・Foundry のセッションは影響を受けません。apiKeyHelperが返す値。ヘルパー自体は正常終了しているのに、空文字や改行つきの文字列を返していることがあります。bash -c "$(あなたのヘルパー)" | od -c | headで末尾まで見てください。- キー名の綴り。
settings.jsonのキーを一文字間違えても、Claude Code は何も言わずに無視します。これは別の記事で扱いました(settings.json のキー名を1文字間違えても、Claude Code は何も言わずに無視します)。
三つとも、症状は「認証だけが通らない」で同じです。だからこそ、入力面を先に一枚にしておく価値があります。
なお、ヘッドレス実行で資格情報が上書きされてしまう方向の話は、別途一時的な401が長期トークンを置き換えるにまとめております。無人で走らせている処理をお持ちの方には、こちらのほうが効くかもしれません。
まず今日は、いま使っている端末で authsurface.sh を一度だけ走らせて、その出力を保存してみてください。差分は次に版を上げた瞬間から使えるようになります。私も、版を上げるたびにこの一手間を挟むようにしております。
版を上げるだけで朝が戻ってくるのであれば、それはそれで悪くない結末なのだと思います。