diff --git a/server.mjs b/server.mjs index 73ed83e..5329b8d 100644 --- a/server.mjs +++ b/server.mjs @@ -471,6 +471,10 @@ async function resolveSpawnDecision() { // piling every request into the real HOME. const release = await realHomeFallbackMutex.acquire(); try { + // Drop the 30s keychain TTL cache so the re-check reads FRESH keychain state — otherwise a + // waiter admitted right after the prior holder's claude refreshed the token could still see the + // stale (expiring) cached creds and needlessly fall back to real HOME again for up to ~30s. + invalidateKeychainReadCache(); const retry = resolveSpawnToken(); if (retry) { release(); @@ -1641,6 +1645,14 @@ function readKeychainCreds() { }); } +// F3 drain helper: drop the F5 keychain TTL cache so the NEXT getOAuthCredentials() re-reads the +// keychain from scratch. Called under the real-HOME fallback mutex just before the re-check, so a +// waiter admitted after the prior holder's claude refreshed the keychain sees the FRESH token +// immediately (and proceeds ISOLATED) instead of waiting out the ≤30s TTL on the stale creds. +function invalidateKeychainReadCache() { + _keychainCache.clear(); +} + function getOAuthCredentials() { // 1. Env var fallback — highest precedence for explicit overrides. if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {