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

OAuth provider "${provider}" does not support token refresh

Error message

OAuth provider "${provider}" does not support token refresh

What it means

When refreshing via a runtime-registered custom OAuth provider, the library checks customProvider.refreshToken. If the provider is registered but has no refreshToken implementation, token refresh cannot proceed and the library throws AIError.OAuthError with kind 'configuration'. This is a provider-registration gap, not a network or token problem.

Source

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

	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);
		const overrideRefresh = this.#refreshOAuthCredentialOverride ?? storeRefresh;
		if (overrideRefresh && credentialId !== undefined) {
			refreshPromise = overrideRefresh(provider, credentialId, credential, signal);
		} else {
			const customProvider = getOAuthProvider(provider);
			if (customProvider) {
				if (!customProvider.refreshToken) {
					throw new AIError.OAuthError(`OAuth provider "${provider}" does not support token refresh`, {
						kind: "configuration",
						provider,
					});
				}
				refreshPromise = customProvider.refreshToken(credential, signal);
			} else {
				refreshPromise = refreshOAuthToken(provider as OAuthProvider, credential, signal);
			}
		}
		// Bound the refresh so a slow/hanging token endpoint cannot stall credential selection.
		// Caller-driven abort jumps the gun on the timeout — the agent's ESC must
		// take priority over the floor timeout.
		const cancellation = Promise.withResolvers<never>();
		let onAbort: (() => void) | undefined;
		const timeout = setTimeout(
			() =>
				cancellation.reject(
					new AIError.OAuthError(`OAuth token refresh timed out for provider: ${provider}`, {

View on GitHub (pinned to 9690622007)

Solutions

  1. Add a refreshToken implementation to your custom OAuth provider registration
  2. If the provider cannot refresh, plan for re-login when the access token expires instead of forcing refresh
  3. Pass overrideRefresh (a provider-capable refresh function) when triggering refresh for this credential
  4. Verify the correct provider id — a builtin provider with refresh support may be shadowed by an incomplete custom registration of the same id

Example fix

// before
registerOAuthProvider({ id: "myprov", login: loginFlow }); // no refreshToken
// after
registerOAuthProvider({
	id: "myprov",
	login: loginFlow,
	refreshToken: async (credential, signal) => refreshViaProvider(credential.refresh, signal),
});
Defensive patterns

Strategy: validation

Validate before calling

const custom = getOAuthProvider(provider);
if (custom && !custom.refreshToken) {
	throw new Error(`${provider} lacks refreshToken; schedule re-login instead`);
}

Try / catch

try {
	return await getCredentialWithRefresh(provider);
} catch (error) {
	if (error instanceof AIError.OAuthError && error.kind === "configuration") {
		await reLogin(provider); // cannot refresh; log in again
	}
	throw error;
}

Prevention

When it happens

Trigger: A stored OAuth credential for a custom (runtime-registered) provider expires and the library attempts refresh, but getOAuthProvider(provider).refreshToken is undefined — the provider was registered with login only, or with refresh support removed.

Common situations: Custom provider registered without a refreshToken function; forcing a refresh (overrideRefresh absent, credentialId present) on a provider that only supports initial login; upstream changed the provider registration and dropped refresh support.

Related errors


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