amruthpillai/reactive-resume · error · ORPCError

BAD_REQUEST

BAD_REQUEST

Error message

Invalid AI provider configuration.

What it means

BAD_REQUEST thrown by the throwAiProviderConfigError helper in the AI router. Reached when an AI feature's catch block detects the sentinel error message 'INVALID_AI_BASE_URL' (isInvalidAiBaseUrlError), indicating the provider's configured baseURL failed normalization during the model call. Functionally the same root cause as error 25 but surfaced from the AI-feature path rather than the provider-management path.

Source

Thrown at packages/api/src/features/ai/router.ts:32

function isInvalidAiBaseUrlError(error: unknown): boolean {
	return error instanceof Error && error.message === "INVALID_AI_BASE_URL";
}

function isAiProviderGatewayError(error: unknown): boolean {
	return error instanceof AISDKError;
}

function isCredentialEncryptionUnavailable(error: unknown): boolean {
	return error instanceof Error && error.message === "AI_CREDENTIAL_ENCRYPTION_UNAVAILABLE";
}

/** Throws a BAD_GATEWAY ORPCError, preserving the original cause for upstream error reporters. */
function throwAiProviderGatewayError(cause?: unknown): never {
	throw new ORPCError("BAD_GATEWAY", { message: "Could not reach the AI provider.", cause });
}

function throwAiProviderConfigError(): never {
	throw new ORPCError("BAD_REQUEST", { message: "Invalid AI provider configuration." });
}

function throwCredentialEncryptionUnavailable(): never {
	throw new ORPCError("PRECONDITION_FAILED", {
		message: "AI providers are unavailable because ENCRYPTION_SECRET is not configured.",
	});
}

function throwResumeStructureError(error: ZodError): never {
	throw new ORPCError("BAD_REQUEST", {
		message: "Invalid resume data structure",
		cause: flattenError(error),
	});
}

async function getRunnableProvider(userId: string, aiProviderId?: string) {
	const provider = aiProviderId
		? await aiProvidersService.getRunnableById({ id: aiProviderId, userId })

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Open the provider in Settings, correct the baseURL to a full https:// URL, and re-test.
  2. Run POST /ai-providers/{id}/test after fixing to confirm the base resolves.
  3. If no aiProviderId was passed, fix the user's default-enabled provider (getDefaultRunnable path).
  4. Add client-side baseURL validation when saving to prevent recurrence.

Example fix

// before
baseURL saved as 'api.openai.com'

// after
baseURL saved as 'https://api.openai.com/v1', then re-tested
Defensive patterns

Strategy: validation

Validate before calling

function assertValidAiBase(baseURL) {
  try { const u = new URL(baseURL); if (u.protocol !== 'https:' && u.protocol !== 'http:') throw 0; }
  catch { throw new Error('INVALID_AI_BASE_URL'); }
}

Type guard

function looksLikeValidAiBaseUrl(s) {
  try { const u = new URL(s); return u.protocol.startsWith('http'); } catch { return false; }
}

Try / catch

try {
  await ai.parsePdf(input);
} catch (e) {
  if (e.code === 'BAD_REQUEST' && /Invalid AI provider configuration/i.test(e.message)) {
    routeTo('/settings/integrations');
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking parsePdf/analyze/etc. with an aiProviderId (or default provider) whose stored baseURL is invalid, so building the model client throws 'INVALID_AI_BASE_URL', which the handler remaps to this message.

Common situations: A provider was saved with a bad baseURL before validation tightened; the user edited baseURL to something malformed and the test step was skipped; provider migration changed the expected base path.

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/93899dce000b9bab. Report an issue: GitHub.