{"record":{"id":"5497320ffad62ef7","repo":"tinyhumansai/openhuman","slug":"openai-codex-oauth-missing-callback-url","errorCode":"OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL","errorMessage":"OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL","messagePattern":"OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/api/aiSettingsApi.ts","lineNumber":734,"sourceCode":"  await authRemoveProviderCredentials({ provider: authKeyForSlug(slug), profile: 'default' });\n}\n\nexport async function startOpenAiCodexOAuth(): Promise<OpenAiCodexOAuthStartResult> {\n  const res = await callCoreRpc<{ result: OpenAiCodexOAuthStartResult }>({\n    method: 'openhuman.inference_openai_oauth_start',\n    params: {},\n  });\n  const authUrl = res?.result?.authUrl?.trim();\n  if (!authUrl) {\n    throw new Error(OPENAI_CODEX_OAUTH_MISSING_AUTH_URL);\n  }\n  return res.result;\n}\n\nexport async function completeOpenAiCodexOAuth(callbackUrl: string): Promise<void> {\n  const callback = callbackUrl.trim();\n  if (!callback) {\n    throw new Error(OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL);\n  }\n  await callCoreRpc({\n    method: 'openhuman.inference_openai_oauth_complete',\n    params: { callback_url: callback },\n  });\n}\n\nexport async function importOpenAiCodexCliAuth(): Promise<void> {\n  await callCoreRpc({ method: 'openhuman.inference_openai_oauth_import_codex_cli', params: {} });\n}\n\n/**\n * Eagerly write the cloud_providers list to the core config.\n *\n * Called immediately when providers are added/edited/removed so that\n * `listProviderModels` can resolve the provider by id without waiting for\n * the user to click the global Save button.  API keys are NOT included here\n * (they're written via `setCloudProviderKey` on their own path).","sourceCodeStart":716,"sourceCodeEnd":752,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/api/aiSettingsApi.ts#L716-L752","documentation":"completeOpenAiCodexOAuth requires the OAuth callback URL (the deep link the browser redirected back to, containing the code/state query). After trimming, an empty value cannot be exchanged for tokens, so the client rejects it before calling openhuman.inference_openai_oauth_complete.","triggerScenarios":"Calling completeOpenAiCodexOAuth('') or completeOpenAiCodexOAuth('   ') — e.g. a deep-link handler that fires on the bare scheme openhuman://oauth without the path/query, or a race where the handler runs before the URL is captured.","commonSituations":"Deep-link registration handling only part of the URL; the callback arriving with query params stripped by a browser/launcher; manual testing that calls the completer without a real redirect; string parsing that extracts the wrong segment and yields empty.","solutions":["Capture the full callback URL from the deep-link event and pass it through unmodified","Guard the handler: if the trimmed URL is empty, ignore the event (it may be an unrelated scheme invocation)","Verify the redirect URI configured in the OAuth app matches the deep link so the full URL survives"],"exampleFix":"// before\nonDeepLink(url => completeOpenAiCodexOAuth(extractPath(url)));\n\n// after\nonDeepLink(url => {\n  const callback = url?.trim();\n  if (!callback) return; // unrelated or incomplete invocation\n  completeOpenAiCodexOAuth(callback).catch(notify);\n});","handlingStrategy":"validation","validationCode":"const callback = typeof callbackUrl === 'string' ? callbackUrl.trim() : '';\nif (callback) await completeOpenAiCodexOAuth(callback);","typeGuard":"const isNonEmptyTrimmed = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;","tryCatchPattern":"try { await completeOpenAiCodexOAuth(callbackUrl); }\ncatch (e) { if (String(e.message).includes('callback')) restartOAuthFlow(); else throw e; }","preventionTips":["Pass the deep-link URL through verbatim — don't re-parse or extract pieces","Ignore empty/partial deep-link invocations instead of forwarding them","Verify the OAuth redirect URI matches the registered deep-link scheme"],"tags":["oauth","validation","deep-link","openai-codex"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}