Merge pull request #446 from hijak/fix/serper-api-key-validation

fix: stop rejecting valid Serper API keys
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-03-18 12:06:36 -03:00
committed by GitHub
2 changed files with 61 additions and 5 deletions

View File

@@ -445,16 +445,22 @@ async function validateAnthropicCompatibleProvider({ apiKey, providerSpecificDat
async function validateSearchProvider(
url: string,
init: RequestInit
): Promise<{ valid: boolean; error: string | null }> {
): Promise<{ valid: boolean; error: string | null; unsupported: false }> {
try {
const response = await fetch(url, init);
if (response.ok) return { valid: true, error: null };
if (response.ok) return { valid: true, error: null, unsupported: false };
if (response.status === 401 || response.status === 403) {
return { valid: false, error: "Invalid API key" };
return { valid: false, error: "Invalid API key", unsupported: false };
}
return { valid: false, error: `Validation failed: ${response.status}` };
// For provider setup we only need to confirm authentication passed.
// Search providers may return non-auth statuses for exhausted credits,
// rate limiting, or request-shape quirks while still accepting the key.
if (response.status < 500) {
return { valid: true, error: null, unsupported: false };
}
return { valid: false, error: `Validation failed: ${response.status}`, unsupported: false };
} catch (error: any) {
return { valid: false, error: error.message || "Validation failed" };
return { valid: false, error: error.message || "Validation failed", unsupported: false };
}
}

View File

@@ -0,0 +1,50 @@
import test from "node:test";
import assert from "node:assert/strict";
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
test("serper validation accepts authenticated non-auth upstream errors", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(JSON.stringify({ error: "credits_exhausted" }), {
status: 402,
headers: { "content-type": "application/json" },
});
try {
const result = await validateProviderApiKey({
provider: "serper-search",
apiKey: "valid-serper-key",
});
assert.equal(result.valid, true);
assert.equal(result.error, null);
assert.equal(result.unsupported, false);
} finally {
globalThis.fetch = originalFetch;
}
});
test("serper validation still rejects unauthorized keys", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(JSON.stringify({ error: "Unauthorized" }), {
status: 403,
headers: { "content-type": "application/json" },
});
try {
const result = await validateProviderApiKey({
provider: "serper-search",
apiKey: "bad-serper-key",
});
assert.equal(result.valid, false);
assert.equal(result.error, "Invalid API key");
assert.equal(result.unsupported, false);
} finally {
globalThis.fetch = originalFetch;
}
});