Skip to content

原文(日本語に翻訳)

並行トークンリフレッシュ試行中にキーチェーンキャッシュから古いOAuthトークンが読み取られる競合状態を修正しました

原文(英語)

Fixed a race condition where stale OAuth tokens could be read from the keychain cache during concurrent token refresh attempts

概要

Claude Code v2.1.0で修正された、OAuth認証の競合状態バグです。以前のバージョンでは、複数のリクエストが同時にトークン有効期限切れを検出してリフレッシュを試みると、キーチェーンキャッシュから古いトークンが読み取られ、一部のリクエストが無効なトークンで実行されていました。この修正により、トークンリフレッシュ処理が適切にロックされ、すべてのリクエストが最新のトークンを使用するようになりました。

修正前の問題

症状

bash
# 複数のタスクを並行実行
claude

> これらのファイルをすべて読んで:
> - file1.js, file2.js, file3.js, ...(10ファイル)

# 並行で10個のReadツール呼び出し
# すべてがほぼ同時にトークン有効期限切れを検出

# 競合状態発生:
# Request 1: トークンリフレッシュ開始 → 成功 → 新トークンをキャッシュに保存
# Request 2: 古いキャッシュトークンを読み取り → 401エラー
# Request 3: 古いキャッシュトークンを読み取り → 401エラー
# ...

# 一部のファイル読み取りが失敗
Error: Unauthorized (401) for file2.js
Error: Unauthorized (401) for file3.js

根本原因

時刻: T0
- トークン有効期限切れ
- キャッシュ: 古いトークン (expired)

時刻: T1(並行リクエスト)
- Request 1: 有効期限チェック → 期限切れ → リフレッシュ開始
- Request 2: キャッシュ読み取り → 古いトークン取得(まだ更新されていない)
- Request 3: キャッシュ読み取り → 古いトークン取得

時刻: T2
- Request 1: リフレッシュ完了 → 新トークンをキャッシュに保存
- Request 2: 古いトークンでAPI呼び出し → 401エラー
- Request 3: 古いトークンでAPI呼び出し → 401エラー

結果: リクエスト2と3が失敗

修正後の動作

適切なロック制御

bash
# 複数のタスクを並行実行
claude

> これらのファイルをすべて読んで:
> - file1.js, file2.js, file3.js, ...(10ファイル)

# 修正後の動作:
# Request 1: トークン有効期限切れを検出 → リフレッシュロック取得 → リフレッシュ開始
# Request 2-10: リフレッシュロックを待機

# Request 1: リフレッシュ完了 → 新トークンをキャッシュに保存 → ロック解放
# Request 2-10: ロック解放を検出 → 新トークンをキャッシュから読み取り → 成功

# ✓ すべてのファイル読み取りが成功

実践例

大量の並行ファイル読み取り

多数のファイルを並行読み取りしても、認証が安定します。

bash
# 100ファイルを並行読み取り
claude

> src/**/*.js のすべてのファイルを分析

# Claudeが100個のReadツールを並行実行
# トークン有効期限切れのタイミング

# 修正前: 一部のリクエストが401エラー
# 修正後: すべてのリクエストが成功
# ✓ 100ファイルすべて正常に読み取り

バックグラウンドタスクの並行実行

複数のバックグラウンドタスクが同時に認証更新しても安定します。

bash
# 複数のバックグラウンドタスクを起動
claude

> テストを実行 <Ctrl+B>
> ビルドを実行 <Ctrl+B>
> リントを実行 <Ctrl+B>

# 3つのタスクが並行実行
# すべてがほぼ同時にトークン有効期限切れ

# 修正前: 一部のタスクが認証エラーで失敗
# 修正後: すべてのタスクが自動リフレッシュ → 成功

エージェントの並行処理

複数のエージェントが並行動作しても認証が安定します。

bash
# 複数のエージェントを並行起動
claude

> /feature-dev を実行

# feature-dev が複数のサブエージェントを並行起動:
# - code-explorer
# - code-architect
# - code-reviewer

# すべてがトークン有効期限切れを検出

# 修正後: 1つがリフレッシュ、他は待機
# ✓ すべてのエージェントが成功

注意点

  • この修正は Claude Code v2.1.0(2026年1月7日リリース)で実装されました
  • 競合状態(Race Condition)とは:
    • 複数の並行処理が共有リソース(キャッシュ)に同時アクセス
    • タイミングによって結果が異なる
    • 非決定的なバグ(再現が難しい)
  • 修正の詳細:
    • トークンリフレッシュ処理にミューテックスロックを導入
    • 最初のリクエストのみリフレッシュを実行
    • 他のリクエストはリフレッシュ完了を待機
    • すべてのリクエストが新しいトークンを使用
  • 影響を受けるシナリオ:
    • 並行ツール実行(複数のReadなど)
    • バックグラウンドタスクの同時実行
    • 複数のエージェントが並行動作
    • 高負荷時の大量リクエスト
  • ロックの動作:
    • 排他ロック(Mutex)を使用
    • リフレッシュ中は他のスレッドがブロック
    • リフレッシュ完了後、すべてのスレッドが新トークンを取得
  • パフォーマンスへの影響:
    • 通常時: 影響なし(ロックなし)
    • リフレッシュ時: 最初のリクエスト以外は数百ミリ秒待機
    • 全体的なスループットは向上(401エラーの再試行が不要)
  • キーチェーンキャッシュ:
    • macOS: Keychain Access
    • Linux: libsecret / gnome-keyring
    • Windows: Credential Manager
    • キャッシュは高速読み取りのため
  • デバッグ:
    • --debug フラグでロック取得/解放のログを確認
    • ログに「Acquired token refresh lock」「Released token refresh lock」が表示される
  • 関連する修正:
    • index 40: サーバー報告による有効期限切れの処理改善
    • index 41: セッション永続化の409競合回復
    • これらの修正と合わせて、認証の信頼性が大幅に向上
  • エラーハンドリング:
    • ロック待機中のタイムアウト: 30秒
    • タイムアウト後は独自にリフレッシュ試行
    • リフレッシュ失敗時は再ログインを要求

関連情報