coleam00/Archon · error

Pi OAuth provider ' ' produced no apiKey for the stored…

Error message

Pi OAuth provider '${oauthAuth.name}' produced no apiKey for the stored credential.

What it means

getApiKey derives fresh auth state from a stored credential via the provider's oauthAuth.toAuth(). toAuth is a pure derivation and does not check expiry, so an apiKey can legitimately be absent — e.g. the stored credentials are incomplete, expired past refresh, or the provider returned no token. The method throws rather than returning an unusable empty key.

Solutions

  1. Delete the stored credential and re-run the provider's login flow to obtain fresh tokens
  2. Inspect the stored credentials for missing/expired fields and refresh them explicitly before calling getApiKey
  3. Verify the installed Pi SDK version matches the adapter (toAuth behavior may have changed)
  4. Check provider-side account status (revoked grants, expired sessions) and re-authorize

Example fix

// before (stale stored credentials)
const { apiKey } = await provider.getApiKey(staleCredentials);
// after (re-login when derivation yields nothing)
let creds = staleCredentials;
const auth = await oauthAuth.toAuth(creds);
if (!auth.apiKey) creds = await login(providerId, callbacks);
const { apiKey } = await provider.getApiKey(creds);
Defensive patterns

Strategy: try-catch

Validate before calling

const auth = await oauthAuth.toAuth(credentials);
const needsRelogin = !auth.apiKey;

Type guard

function hasApiKey(a: { apiKey?: string }): a is { apiKey: string } {
  return typeof a.apiKey === 'string' && a.apiKey.length > 0;
}

Try / catch

try {
  return await provider.getApiKey(credentials);
} catch (err) {
  if (String(err.message).includes('produced no apiKey')) {
    const fresh = await runLogin(providerId); // re-authenticate
    return provider.getApiKey(fresh);
  }
  throw err;
}

Prevention

When it happens

Trigger: getApiKey(credentials) is invoked with a stored OAuth credential for which toAuth() returns an object with no apiKey (refresh failed to yield a token, credentials shape changed, or provider returned partial auth state).

Common situations: Long-lived stored credentials expired beyond refresh window; provider rotated its token response shape after an SDK update; credentials persisted by an older version missing fields the new toAuth expects; corrupted/partial credential row in the store.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/9ff83fedbd6e8f47. Report an issue: GitHub.

Appendix: source

Thrown at packages/providers/src/oauth.ts:190

  return {
    id,
    usesCallbackServer,
    async login(callbacks): Promise<OAuthCredential> {
      const oauthAuth = await loader();
      return oauthAuth.login(adaptLoginCallbacks(oauthAuth, callbacks));
    },
    async refreshToken(credentials, options): Promise<OAuthCredential> {
      const oauthAuth = await loader();
      const signal = options?.signal ?? new AbortController().signal;
      return oauthAuth.refresh(credentials, signal);
    },
    async getApiKey(credentials): Promise<{ apiKey: string }> {
      const oauthAuth = await loader();
      // toAuth is a side-effect-free derivation ({ apiKey?, headers?, baseUrl? })
      // from whatever credential it is given — it does NOT check expiry.
      const auth = await oauthAuth.toAuth(credentials);
      if (!auth.apiKey) {
        throw new Error(
          `Pi OAuth provider '${oauthAuth.name}' produced no apiKey for the stored credential.`
        );
      }
      return { apiKey: auth.apiKey };
    },
  };
}

export const anthropicOAuthProvider: OAuthProviderInterface = adaptOAuthAuth(
  'anthropic',
  () => loadOAuthAuth('anthropic.js', 'anthropicOAuth'),
  true
);
export const githubCopilotOAuthProvider: OAuthProviderInterface = adaptOAuthAuth(
  'github-copilot',
  () => loadOAuthAuth('github-copilot.js', 'githubCopilotOAuth'),
  false
);

View on GitHub (pinned to 0773b97458)