tinyhumansai/openhuman · error · Error

OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL

OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL

Error message

OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL

What it means

Thrown by completeOpenAiCodexOAuth() before any RPC is sent, when the callbackUrl argument is empty or only whitespace after trimming. The function expects the full redirect URL (containing the OAuth code/state) captured from the browser or deep link after the user authorizes; passing anything blank cannot be completed. This is pure input validation — the core is never contacted.

Solutions

  1. Log/inspect the callbackUrl at the call site — it must be the complete redirect URL including ?code=...&state=... before calling completeOpenAiCodexOAuth.
  2. Fix the extraction: for deep links read the URL from the Tauri deep-link event payload, not window.location, and check the param name the core's redirect uses.
  3. Verify the OAuth start's redirect URI matches what is registered so the callback actually carries code/state.
  4. Guard the UI flow: only call complete when a non-empty callback exists; otherwise restart the OAuth flow with startOpenAiCodexOAuth().

Example fix

// before
export async function completeOpenAiCodexOAuth(callbackUrl: string): Promise<void> {
  const callback = callbackUrl.trim();
  if (!callback) {
    throw new Error(OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL);
  }
  await callCoreRpc({ method: 'openhuman.inference_openai_oauth_complete', params: { callback_url: callback } });
}

// after — caller validates and reuses the OAuth start value
const callback = extractCallbackUrl(deepLinkEvent); // may be ''
if (!callback) {
  await startOpenAiCodexOAuth(); // restart the flow instead of throwing
} else {
  await completeOpenAiCodexOAuth(callback);
}
Defensive patterns

Strategy: validation

Validate before calling

const callback = typeof deepLinkUrl === 'string' ? deepLinkUrl.trim() : '';
if (!callback || !callback.includes('code=')) {
  // do not call completeOpenAiCodexOAuth — restart the flow instead
  await startOpenAiCodexOAuth();
} else {
  await completeOpenAiCodexOAuth(callback);
}

Type guard

function isNonEmptyCallbackUrl(url: unknown): url is string {
  return typeof url === 'string' && url.trim().length > 0 && /^https?:\/\/|^[a-z]+:\/\//i.test(url.trim());
}

Try / catch

try {
  await completeOpenAiCodexOAuth(callback);
} catch (e) {
  if (e instanceof Error && e.message === 'OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL') {
    // restart OAuth instead of surfacing an internal validation error
    await startOpenAiCodexOAuth();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling completeOpenAiCodexOAuth(''), completeOpenAiCodexOAuth(' '), or completeOpenAiCodexOAuth(undefined as any); a deep-link handler extracting the callback URL from a URL fragment/query that is absent or misnamed, yielding an empty string.

Common situations: Deep-link registration missing or the OS delivered a bare openhuman:// host without query params; the OAuth redirect_uri mismatch so the app opens without code/state; a refactor changed where the callback URL is read from (window.location.href vs event payload) and it is now undefined coerced to ''.string.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/5497320ffad62ef7. Report an issue: GitHub.

Appendix: source

Thrown at app/src/services/api/aiSettingsApi.ts:734

  await authRemoveProviderCredentials({ provider: authKeyForSlug(slug), profile: 'default' });
}

export async function startOpenAiCodexOAuth(): Promise<OpenAiCodexOAuthStartResult> {
  const res = await callCoreRpc<{ result: OpenAiCodexOAuthStartResult }>({
    method: 'openhuman.inference_openai_oauth_start',
    params: {},
  });
  const authUrl = res?.result?.authUrl?.trim();
  if (!authUrl) {
    throw new Error(OPENAI_CODEX_OAUTH_MISSING_AUTH_URL);
  }
  return res.result;
}

export async function completeOpenAiCodexOAuth(callbackUrl: string): Promise<void> {
  const callback = callbackUrl.trim();
  if (!callback) {
    throw new Error(OPENAI_CODEX_OAUTH_MISSING_CALLBACK_URL);
  }
  await callCoreRpc({
    method: 'openhuman.inference_openai_oauth_complete',
    params: { callback_url: callback },
  });
}

export async function importOpenAiCodexCliAuth(): Promise<void> {
  await callCoreRpc({ method: 'openhuman.inference_openai_oauth_import_codex_cli', params: {} });
}

/**
 * Eagerly write the cloud_providers list to the core config.
 *
 * Called immediately when providers are added/edited/removed so that
 * `listProviderModels` can resolve the provider by id without waiting for
 * the user to click the global Save button.  API keys are NOT included here
 * (they're written via `setCloudProviderKey` on their own path).

View on GitHub (pinned to 7491200858)