diff --git a/apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx b/apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx index e16093d92..4f438f72e 100644 --- a/apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx +++ b/apps/ui/src/components/provider-keys/create-provider-key-dialog.tsx @@ -196,12 +196,14 @@ export function CreateProviderKeyDialog({ void queryClient.invalidateQueries({ queryKey }); setOpen(false); }, - onError: () => { + onError: (error) => { setIsValidating(false); toast({ title: "Validation Failed", description: - "Failed to validate the API key. Please check your key and region.", + error instanceof Error + ? error.message + : "Failed to validate the API key. Please check your key and region.", variant: "destructive", }); }, diff --git a/packages/actions/src/validate-provider-key.ts b/packages/actions/src/validate-provider-key.ts index b3d420c89..149d50aaf 100644 --- a/packages/actions/src/validate-provider-key.ts +++ b/packages/actions/src/validate-provider-key.ts @@ -278,6 +278,25 @@ export async function validateProviderKey( }; } + // Treat billing/quota errors as valid key — the key is authenticated + // but the account has insufficient credits or hit rate limits + const isBillingError = + response.status === 402 || + response.status === 429 || + errorMessage.toLowerCase().includes("credit balance") || + errorMessage.toLowerCase().includes("billing") || + errorMessage.toLowerCase().includes("quota") || + errorMessage.toLowerCase().includes("rate limit"); + + if (isBillingError) { + logger.debug("Provider key is valid but has billing/quota issues", { + provider, + model: validationModel, + statusCode: response.status, + }); + return { valid: true, model: validationModel }; + } + return { valid: false, error: errorMessage,