can1357/oh-my-pi · error · AIError.OAuthError
OAuth credential no longer exists for provider: ${provider}
Error message
OAuth credential no longer exists for provider: ${provider} What it means
Thrown as AIError.OAuthError with kind 'token-refresh' when the credential that was about to be refreshed no longer exists in storage — it was deleted between the caller's read and the refresh request. The library cannot refresh a row that is gone, so it fails definitively rather than silently recreating it.
Source
Thrown at packages/ai/src/auth-storage.ts:5263
// are legitimately short-lived); the next resolve simply treats it as
// due for refresh again instead of rejecting a token we just minted.
if (Date.now() < result.credential.expires) return result.credential;
} else if (Date.now() + OAUTH_REFRESH_SKEW_MS < result.credential.expires) {
// Reloaded (not refreshed by us) credential — match #refreshOAuthCredential's
// freshness contract: a reload within the refresh skew still counts as
// needing refresh, so returning it here would make the final candidate pass
// refresh the same row again and replay the token we just failed on.
return result.credential;
}
throw new AIError.OAuthError(
`OAuth refresh did not produce a usable credential for provider: ${provider}`,
{
kind: "token-refresh",
provider,
},
);
}
throw new AIError.OAuthError(`OAuth credential no longer exists for provider: ${provider}`, {
kind: "token-refresh",
provider,
});
}
return this.#requestOAuthCredentialRefresh(provider, credential, credentialId, signal);
}
async #requestOAuthCredentialRefresh(
provider: Provider,
credential: OAuthCredential,
credentialId: number | undefined,
signal?: AbortSignal,
): Promise<OAuthCredentials> {
let refreshPromise: Promise<OAuthCredentials>;
// Caller override > store-level hook > local per-provider refresh.
// `RemoteAuthCredentialStore` exposes the hook so a broker-backed gateway
// routes refresh through the broker without explicit wiring.
const storeRefresh = this.#store.refreshOAuthCredential?.bind(this.#store);View on GitHub (pinned to 9690622007)
Solutions
- Re-run the OAuth login flow — the credential must be re-established before any request can succeed
- Coordinate logout/credential removal across processes so it doesn't race in-flight requests
- Catch this (kind 'token-refresh') and re-fetch credentials from storage before retrying
- If it happens repeatedly, check for another process or job deleting credentials (cleanup scripts, multi-instance setups)
Example fix
// before
const access = await withOAuthAccess(storage, "codex", call); // thrown mid-refresh
// after
try {
return await withOAuthAccess(storage, "codex", call);
} catch (error) {
if (error instanceof AIError.OAuthError && error.kind === "token-refresh") {
if (!(await storage.getOAuthAccess("codex"))) await reLogin("codex");
return withOAuthAccess(storage, "codex", call);
}
throw error;
} Defensive patterns
Strategy: type-guard
Validate before calling
const access = await storage.getOAuthAccess(provider); if (!access) await reLogin(provider); // row gone before refresh
Type guard
function isCredentialGoneError(e: unknown): e is AIError.OAuthError {
return e instanceof AIError.OAuthError &&
e.kind === "token-refresh" &&
e.message.includes("no longer exists");
} Try / catch
try {
return await withOAuthAccess(storage, provider, attempt);
} catch (error) {
if (isCredentialGoneError(error)) await reLogin(provider);
throw error;
} Prevention
- Avoid logging out / deleting credentials while requests are in flight
- Serialize logout and refresh through one owner per provider
- Re-read credentials from storage after any concurrent operation instead of caching rows
When it happens
Trigger: Calling the refresh path (e.g. getOAuthApiKey triggering a needed refresh, or #requestOAuthCredentialRefresh by credentialId) after another code path — logout, removeCredential, another process — deleted the credential row for that provider.
Common situations: User logged out in another window/process while a request was in flight; concurrent session cleanup removing the credential; rotating credentials in one tab while another tab refreshes the old one.
Related errors
- OAuth refresh ownership was lost before persistence
- OAuth refresh did not produce a usable credential for provid
- No OAuth credential available for provider: ${provider}
- OAuth refresh ownership aborted by caller
- Unknown OAuth provider: ${provider}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/1c7fadd5a380e108.
Report an issue: GitHub.