{"record":{"id":"e76a88ddeaafdaa9","repo":"can1357/oh-my-pi","slug":"unknown-oauth-provider-provider","errorCode":null,"errorMessage":"Unknown OAuth provider: ${provider}","messagePattern":"Unknown OAuth provider: (.+?)","errorType":"exception","errorClass":"AIError.ConfigurationError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/auth-storage.ts","lineNumber":2985,"sourceCode":"\t\t\t/** onPrompt is required for some providers (github-copilot, openai-codex) */\n\t\t\tonPrompt: (prompt: { message: string; placeholder?: string }) => Promise<string>;\n\t\t},\n\t): Promise<OAuthLoginIdentity | undefined> {\n\t\t// Only paste-code providers (fixed non-loopback redirect, e.g. GitLab Duo\n\t\t// Agent's vscode:// URI) get a default manual-code prompt. For loopback OAuth\n\t\t// providers the `OAuthCallbackFlow` would otherwise race this readline prompt\n\t\t// against the HTTP callback and, when the callback wins, leave the prompt\n\t\t// outstanding — a dirty/blocked terminal. Synthesizing the default only for\n\t\t// paste-code providers is the authoritative gate (it covers every caller, not\n\t\t// just the CLI); an explicit caller-supplied `onManualCodeInput` is still\n\t\t// honored for any provider as an escape hatch.\n\t\tconst manualCodeInput = PASTE_CODE_LOGIN_PROVIDERS.has(provider)\n\t\t\t? () => ctrl.onPrompt({ message: \"Paste the authorization code (or full redirect URL):\" })\n\t\t\t: undefined;\n\t\t// Built-in registry first, then runtime-registered extension providers.\n\t\tconst def = getProviderDefinition(provider) ?? getOAuthProvider(provider);\n\t\tif (!def?.login) {\n\t\t\tthrow new AIError.ConfigurationError(`Unknown OAuth provider: ${provider}`);\n\t\t}\n\t\tconst result = await def.login({\n\t\t\tonAuth: ctrl.onAuth,\n\t\t\tonProgress: ctrl.onProgress,\n\t\t\tonPrompt: ctrl.onPrompt,\n\t\t\tonManualCodeInput: ctrl.onManualCodeInput ?? manualCodeInput,\n\t\t\tsignal: ctrl.signal,\n\t\t\tfetch: ctrl.fetch,\n\t\t});\n\t\tif (typeof result === \"string\") {\n\t\t\t// Some flows (e.g. ollama) return \"\" to signal that no key was entered.\n\t\t\tif (!result) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tconst newCredential: ApiKeyCredential = { type: \"api_key\", key: result, source: \"login\" };\n\t\t\tconst stored = this.#store.upsertAuthCredentialRemote\n\t\t\t\t? await this.#store.upsertAuthCredentialRemote(provider, newCredential)\n\t\t\t\t: this.#store.upsertAuthCredentialForProvider(provider, newCredential);","sourceCodeStart":2967,"sourceCodeEnd":3003,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/auth-storage.ts#L2967-L3003","documentation":"loginOAuth resolves the provider against the built-in provider registry (getProviderDefinition) and then runtime-registered extension providers (getOAuthProvider). If neither exists, or the definition has no login implementation, the provider is unknown/not login-capable and the library throws ConfigurationError.","triggerScenarios":"Calling loginOAuth / the login control flow with a provider string that is not in the built-in registry and has no runtime-registered OAuth provider with a login() function — e.g. a typo, or a custom provider registered without a login implementation.","commonSituations":"Misspelled provider id (case-sensitive); attempting OAuth login for a provider that only supports API keys; custom OAuth provider registered via registerOAuthProvider without defining login; calling login before the extension provider registration ran.","solutions":["Check the provider id spelling against the supported provider list (exact match)","Register your custom provider with a login implementation via the runtime OAuth provider registry before calling login","If the provider only supports API keys, use API-key storage instead of the OAuth login flow","Ensure extension-provider registration code runs before any login attempt (import order/init timing)"],"exampleFix":"// before\nawait storage.loginOAuth(\"antrophic\", ctrl); // typo\n// after\nawait storage.loginOAuth(\"anthropic\", ctrl);","handlingStrategy":"validation","validationCode":"import { getOAuthProvider } from \"@oh-my-pi/pi-ai\";\nif (!getOAuthProvider(provider)?.login && !BUILTIN_PROVIDERS.has(provider)) {\n\tthrow new Error(`${provider} is not a login-capable OAuth provider`);\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait storage.loginOAuth(provider, ctrl);\n} catch (error) {\n\tif (error instanceof AIError.ConfigurationError && /Unknown OAuth provider/.test(error.message)) {\n\t\tshowProviderListToUser();\n\t}\n\tthrow error;\n}","preventionTips":["Validate provider ids against the registry before invoking login","Register extension providers at startup, before any login call","Use constants/enums for provider ids instead of ad-hoc strings"],"tags":["oauth","configuration","unknown-provider","login"],"backgroundTag":"unknown-oauth-provider","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}