Skip to content

claude: 馃悰 Read the Keychain before the credentials file - #14

Merged
yasyf merged 3 commits into
mainfrom
spawnllm-cred-order
Oct 7, 2026
Merged

yasyf merged 3 commits into
mainfrom
spawnllm-cred-order

Conversation

@yasyf

@yasyf yasyf commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Isolated Claude runs on this Mac fail with Failed to authenticate: OAuth token revoked: 145 of 200 capt-hook haiku triage calls failed, and the session reviewer's judge logs failed=40 on every pass. Every host reads ~/.claude/.credentials.json first and the Keychain only when that file is missing. Here the file holds a token that expired on Sep 30, while the Keychain item Claude Code keeps fresh is valid.

Claude Code 2.1.293 does the opposite. Its secure storage wraps the Keychain with a plaintext fallback, M(keychain, plaintext) in the bundle, so it reads the Keychain item first and the file only when the item is missing. This PR flips all three hosts (Python, Go, Rust) to that order, and adds a fallthrough for when a token is rejected anyway.

  • claude_isolation_seed now takes credentials_json as an ordered list plus rejected_tokens, and seeds the first access token not yet rejected. When every token has been rejected, it seeds the first one so the run reports the real error. It parses a source only when it gets to it, so a corrupt file behind a good Keychain item no longer matters.
  • A new claude_auth_rejected core op decides whether a run's error is an auth rejection: Failed to authenticate, a revoked OAuth token, authentication_error, or API Error: 401.
  • On a rejection, each host records the token, reseeds, and reruns once per remaining source. These reruns don't count against max_attempts, so a call with attempts=1 still falls through. A rejected token stays skipped for the rest of the backend (Python) or process (Go, Rust).
  • The Python CliBackend splits execute and aexecute into an execute_invocation step that takes the env, so the Claude backend knows which token a run used and an overridden env() still applies.

The core gains 10 golden vectors, 4 for the seed and 6 for the rejection op. Each host has a test where a fake claude rejects the Keychain token, and the run succeeds on the credentials file within one attempt. Python also covers async and the case where every source is rejected. Before the fix, the installed 0.17.0 reproduced the failure on this Mac with claude exited 1: Failed to authenticate: OAuth token revoked. ~/.claude/.credentials.json is never written or deleted.

yasyf added 3 commits October 7, 2026 12:56
Claude-Session-Id: 899e5f7a-9099-4d8f-b96d-79291e63b5b4
Claude-Session-Id: 899e5f7a-9099-4d8f-b96d-79291e63b5b4
Claude-Session-Id: 899e5f7a-9099-4d8f-b96d-79291e63b5b4
@yasyf
yasyf merged commit d63b995 into main Oct 7, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant