can1357/oh-my-pi · error · AIError.OAuthError

OAuth refresh did not produce a usable credential for provid

Error message

OAuth refresh did not produce a usable credential for provider: ${provider}

What it means

Thrown as AIError.OAuthError with kind 'token-refresh' when an OAuth token refresh completes but does not yield a usable credential (and it is not a reload of an already-refreshed credential). The library treats this as a definitive refresh failure for the provider so the retry layer can stop replaying the failed token.

Source

Thrown at packages/ai/src/auth-storage.ts:5255

					),
				isDefinitiveFailure: error => AIError.isDefinitiveOAuthFailure(String(error)),
				disabledCause: error => `oauth refresh failed: ${String(error)}`,
			});
			if (result.credential) {
				if (result.refreshed) {
					// We performed this refresh ourselves — trust the provider's new token
					// even when its lifetime is shorter than the refresh skew (some grants
					// 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,

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-run the full OAuth login flow for the provider to obtain a new refresh token
  2. Check the provider account status — revoked apps/sessions produce empty refresh results
  3. Inspect provider API responses/logs for why the refresh returned no token
  4. Retry the request; withOAuthAccess treats kind 'token-refresh' as a replay-then-stop signal, so re-authentication is the real fix

Example fix

// before
const access = await withOAuthAccess(storage, "codex", call); // OAuthError token-refresh
// after
try {
	return await withOAuthAccess(storage, "codex", call);
} catch (error) {
	if (error instanceof AIError.OAuthError && error.kind === "token-refresh") {
		await reLogin(provider); // refresh token is dead; get a new one
		return withOAuthAccess(storage, "codex", call);
	}
	throw error;
}
Defensive patterns

Strategy: try-catch

Type guard

function isTokenRefreshError(e: unknown): e is AIError.OAuthError {
	return e instanceof AIError.OAuthError && e.kind === "token-refresh";
}

Try / catch

try {
	return await withOAuthAccess(storage, provider, attempt);
} catch (error) {
	if (isTokenRefreshError(error)) {
		await reLogin(provider); // refresh produced nothing usable
		return withOAuthAccess(storage, provider, attempt);
	}
	throw error;
}

Prevention

When it happens

Trigger: A refresh attempt (or reload path) returns a result whose credential is undefined/unusable — e.g. the refresh succeeded server-side but produced no storable credential, or the row was removed mid-refresh so no candidate remains.

Common situations: Provider revoked the refresh token or the account (refresh returns empty); refresh endpoint returns 200 with a malformed/absent token payload; concurrent logout removing the credential between refresh start and finish.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/68d9f252f1feb06b. Report an issue: GitHub.