Claude Media
claude -pがOAuth session expiredで失敗する原因と回避策

claude -pがOAuth session expiredで失敗する原因と回避策

対話モードは動くのにclaude -pだけ「OAuth session expired and could not be refreshed」で止まる。エラーの位置づけ、未解決の報告、無人実行向けの回避策をまとめます。

claude -pを実行するとFailed to authenticate: OAuth session expired and could not be refreshedと表示されて終了する。同じ端末でclaudeを対話モードで起動すると、ログインを求められずに使える。この食い違いは、GitHubのissue #81937で2026年7月から報告が続いており、現在もopenのままです。

先に結論を書きます。この文言は、保存済みのclaude.aiログインを更新できなかったことを示します。対処は/loginのやり直しです。ただし、対話モードが動いているのに-pだけ失敗する理由は、認証やエラーのページに説明がありません。無人で回すジョブでは、サブスクリプションのログインに頼らない認証へ切り替えるのが現実的な逃げ道になります。

このメッセージが意味すること

Claude Codeのエラー一覧では、-pとAgent SDKでこの文言が出る状態をLogin expiredと同じ項目に載せています。対話モードではLogin expired · Please run /loginと表示され、-pでは先頭にFailed to authenticate:が付いた文言になります。構造化エラーコードはauthentication_failedです。

仕組みは次のとおりです。保存済みのリフレッシュトークンをOAuthサービスが拒否すると、Claude Codeは保存済みの認証情報を消します。以後のモデルリクエストはAPIに送られず、ローカルで止まります。

表示出る場面何が起きたか
Login expired · Please run /login出る場面対話モード何が起きたか更新に失敗し、認証情報を消した後で止まっている
Failed to authenticate: OAuth session expired and could not be refreshed出る場面-p・Agent SDK何が起きたか同じ状態の非対話版
OAuth token revoked など出る場面両方何が起きたかAPIが401を返した。別の状態

401をAPIが返すエラーとは別物なので、OAuth token revokedの対処は「OAuth token revoked」の対処と使い分けます。

APIキー、CLAUDE_CODE_OAUTH_TOKEN、クラウドプロバイダーの認証を使っているセッションは保存済みログインを使わないので、このメッセージは出ません。逆に言うと、このメッセージが出ているなら、その-pプロセスは保存済みのサブスクリプションログインで認証しようとしています。

issue #81937で報告されている症状

起票者の環境はmacOSのTerminal.appで、Claude Codeはclaude updateで最新と確認したv2.1.220です。claude -p "say hello" --permission-mode bypassPermissionsが即座に失敗し、同じ端末・同じディレクトリで起動した対話モードは問題なく動いたと書かれています。claude updateをしても結果は変わらず、「以前は動いた」ことのない環境で、リグレッションではないとされています。

起票者は、一度だけ別の文言も見ています。API Error: 401とOAuth access token has been revoked.です。同じログインについて、-pが違う面で別の拒否を受けていた可能性があります。

もう一つ、起票者が書き添えた気になる点があります。対話モードの起動バナーはv2.1.119と表示し、claude updateはインストール済みをv2.1.220と報告したそうです。起票者自身も、関連があるか分からないと書いています。2つのバージョン表示がずれるときは、which -a claudeでPATH上の実行ファイルが複数ないかを確かめる価値があります。

コメントでは、次の報告が続きました。

日付別の利用者からの報告
2026-08-21別の利用者からの報告T3 Codeからの初回利用で同じエラー
2026-08-22別の利用者からの報告対話セッションで/loginを実行したら直った
2026-08-28別の利用者からの報告claude -pでローカル自動化を回しており、毎日の再認証は成り立たないという指摘
2026-08-29別の利用者からの報告2〜3日に一度は遭遇する
2026-09-21別の利用者からの報告同じ問題が出ているという追記
2026-09-22別の利用者からの報告詳細な切り分けの報告(次の節)

/loginで直ったという報告と、直らないという報告が両方あります。対話モードでの/loginが効くかどうかは、環境によって分かれるようです。

長く運用している利用者が切り分けた4点

9月22日のコメントは、MCPコネクタを1つずつ付けたclaude -pの子セッションを、ローカルのオーケストレーターから起動する運用です。9月2日、3日、12日、21日、22日に同じエラーが再発しており、報告者は次の点を除外しています。

  • アカウント側の許可が切れているわけではない。9月21日に設定画面のConnectorsで再認可しても、次の-pが同じ失敗をした
  • 対話セッションの側からは異常が見えない。対話の中のツールではコネクタがconnectedと出ていた
  • 特定のコネクタに限らない。Atlassian、Microsoft 365、Zoomで同じ文言が出た
  • アプリを再起動しても直らない。同じ呼び出しを同日に3〜5回繰り返しても直らない

報告者はここから、-pの子プロセスがアカウントや対話セッションとは別の認証情報を持っている可能性を推測しています。あくまで報告者の推測で、ドキュメントにも修正の記録にも裏づけはありません。同じ文言で報告された#79685はclosedになっていますが、修正を示す記録はありません。

公式ドキュメントから言えること

認証ページには、切り分けに使える記述がいくつかあります。症状の原因を決める材料にはなりませんが、疑う順番は決められます。

保存先は環境ごとに違います。macOSでは暗号化されたKeychain、Linuxは~/.claude/.credentials.json、Windowsは%USERPROFILE%\.claude\.credentials.jsonです。Keychainが書き込みを拒否すると、macOSでもファイルに保存されます。CLAUDE_CONFIG_DIRを設定していると、ファイルもKeychainのエントリもそのディレクトリ単位になり、設定が違うセッションは別のエントリを読みます。

cronやlaunchdからclaude -pを起動するジョブは、環境変数が対話シェルと違います。CLAUDE_CONFIG_DIRやHOMEがずれていれば、対話モードと別の認証情報を見ている可能性があります。これはissueの報告者が検証した原因ではなく、公式の保存先の仕様から導ける切り分けの観点です。

もう一つは同時更新の競合です。同じマシンの複数のClaude Codeプロセスは更新を順番に行い、別のプロセスが更新中だと待たされます。この場合は別の文言(Failed to refresh OAuth token: another Claude Code process is refreshing it ...、コードserver_error)が出て、対処は「少し待って再試行」とされています。今回のcould not be refreshedは、競合ではなく更新の拒否です。

症状を切り分ける手順

手順

-pが失敗したときの確認順

  1. 1

    環境変数を確認する

    認証元になる環境変数が残っていないかを見ます。env -iで空の環境にすると、対話シェルの設定を切り離せます。

  2. 2

    /statusで認証元を見る

    対話モードの/statusで、使われている認証元とLoginの行を見ます。ログインが更新できない状態なら、Expired — log in againと出ます。

  3. 3

    対話で/loginをやり直す

    対話モードでclaudeを起動し、/loginを実行してログインし直します。終わったら、もう一度claude -pを試します。これでも直らない、または数日後に再発するなら、次の節の認証方式の切り替えに進みます。

次のコマンドは、変数の残りと実行ファイルの重複を確かめる例です。

env | grep -E 'ANTHROPIC|CLAUDE_(CODE|CONFIG)'
which -a claude
claude --version
env -i HOME="$HOME" PATH="$PATH" \
  claude -p "reply with exactly: ok"

最後の行は、issue #79685の報告者が再現に使った形です。空の環境でも失敗するなら、ジョブの環境変数が原因ではなく、保存済みログインの状態そのものを疑います。

無人実行で認証を切り替える3つの経路

/loginは人が操作する前提なので、cronやCIで回すジョブの根本対策にはなりません。エラー一覧は、自動化で対話ログインができない場合にANTHROPIC_API_KEYかclaude setup-tokenで作る長期トークンを勧めています。

経路設定するもの注意点
APIキー設定するものANTHROPIC_API_KEY注意点-pでは設定されていれば常に使われる。キーはConsoleで作る
長期トークン設定するものCLAUDE_CODE_OAUTH_TOKEN注意点有効期間1年。Remote Controlとclaude.aiコネクタは使えない
キーを返すスクリプト設定するものapiKeyHelper注意点金庫から短期の認証情報を取れる構成に向く

どれを選ぶかは、サブスクリプションの枠で動かしたいか、ConsoleのAPIとして動かしたいかで分かれます。長期トークンはサブスクリプションの認証で、Pro、Max、Team、Enterpriseのいずれかが必要です。

長期トークンは次の手順で作ります。コマンドはトークンを表示するだけで保存しないので、表示された値を自分で控えます。

claude setup-token
export CLAUDE_CODE_OAUTH_TOKEN=your-token
claude -p "reply with exactly: ok"

この変数は認証元の優先順位で5番目にあたり、保存済みログイン(7番目)より先に読まれます。つまり設定した時点から、-pは保存済みログインを使いません。今回のエラーとは無縁になる代わりに、トークンが1年で切れるため、更新の運用が必要です。更新の手順はClaude Codeログイン方法3種の使い分けにまとめています。

--bareを付ける場合は注意が必要です。bare modeはOAuth資格情報もKeychainも読まないので、CLAUDE_CODE_OAUTH_TOKENは効きません。ANTHROPIC_API_KEYかapiKeyHelperで認証します。

先ほどのコメントのように、claude.aiのコネクタを-pから使っているなら、長期トークンは選べません。コネクタを取得できないためです。ローカルで設定したMCPサーバーは引き続き使えます。コネクタが必須の運用には、issueにもエラー一覧にも使える回避策が載っていません。

失敗を検知して人に知らせるラッパー

無人ジョブでは、失敗に気づくまでの時間が実害になります。#79685の起票者は、スーパーバイザーのログにexit=1が残るだけで、原因が分かりにくかったと書いています。9月22日のコメントでは、停止した実行が約54時間放置されました。

終了コードとメッセージで判定して通知する、最小のラッパーの例です。通知コマンドは環境に合わせて置き換えます。

#!/usr/bin/env bash
out=$(claude -p "$1" 2>&1); code=$?
if [ "$code" -ne 0 ] && \
   echo "$out" | grep -q 'could not be refreshed'; then
  notify "claude -p: 再ログインが必要です"
fi
printf '%s\n' "$out"
exit "$code"

文字列でも判定するのは、ドキュメントにこのケース固有の終了コードの記載がないためです。メッセージの文言は将来変わる可能性があります。判定が外れても「非0なら通知」は働くように作っておくと、見逃しを防げます。

CIでの組み込みはClaude CodeをGitHub Actionsに組み込む、-pの基本はClaude Code -pモードでスクリプトやパイプラインを自動化する基本で扱っています。ほかの認証エラーとあわせて見たいときはClaude Codeでよくあるエラー10選も使えます。

まとめ

この文言は、保存済みログインの更新が拒否された状態を指し、対話モードでの/loginが最初の手です。それで直らない、あるいは数日おきに再発する環境では、-pだけがログインを失う理由はissue #81937で未解決のままです。無人ジョブは、APIキーか長期トークンに切り替えて、保存済みログインへの依存を切れます。コネクタを使う運用だけは、切り替え先がありません。

この記事を共有:XはてブLinkedIn