fix(providers): preserve connection test status codes (#10272)

This commit is contained in:
Aman
2026-08-15 21:14:26 -06:00
committed by GitHub
parent dd4a33d1d8
commit 462f4fc9da
4 changed files with 101 additions and 10 deletions

View File

@@ -0,0 +1 @@
- **fix(providers):** preserve validator HTTP status codes in API-key and web connection-test results so callers can distinguish authentication, rate-limit, and upstream failures ([#10272](https://github.com/diegosouzapw/OmniRoute/pull/10272)) — thanks @Zartharas

View File

@@ -0,0 +1,28 @@
export interface ApiKeyValidationResult {
valid: boolean;
warning?: string | null;
statusCode?: number | null;
deployments?: unknown;
}
export interface ApiKeyTestDiagnosis {
type: string;
source: string;
message: string | null;
code: string | null;
}
export function buildApiKeyConnectionTestResult(
result: ApiKeyValidationResult,
error: string | null,
diagnosis: ApiKeyTestDiagnosis
) {
return {
valid: !!result.valid,
error,
warning: result.warning || null,
statusCode: result.valid ? null : (result.statusCode ?? null),
diagnosis,
...(Array.isArray(result.deployments) ? { deployments: result.deployments } : {}),
};
}

View File

@@ -23,6 +23,7 @@ import { isGitLabDirectAccessDisabled } from "@/lib/oauth/gitlab";
import { providerAllowsOptionalApiKey } from "@/shared/constants/providers";
import { removeConnectionHealth } from "@omniroute/open-sse/services/apiKeyRotator.ts";
import { classifyAmbiguousOrAuthError, type ClassifyFailureArgs } from "./mistralAmbiguousAuth";
import { buildApiKeyConnectionTestResult } from "./apiKeyTestResult";
import { OAUTH_TEST_CONFIG } from "./oauthTestConfig";
// Bound the OAuth probe so a hung upstream can't block the connection-test queue
@@ -614,15 +615,7 @@ async function testApiKeyConnection(connection: any) {
? makeDiagnosis("ok", "upstream", null, null)
: classifyFailure({ error, statusCode: result.statusCode, provider: connection.provider });
return {
valid: !!result.valid,
error,
warning: result.warning || null,
diagnosis,
...(Array.isArray((result as any).deployments)
? { deployments: (result as any).deployments }
: {}),
};
return buildApiKeyConnectionTestResult(result, error, diagnosis);
}
/**
@@ -709,7 +702,8 @@ export async function testSingleConnection(connectionId: string, validationModel
// failures a short cooldown so the lazy-recovery path retries them.
const terminalTestStatuses = new Set(["banned", "expired", "credits_exhausted"]);
const isTerminalFailure =
!result.valid && terminalTestStatuses.has(String(diagnosis.code ?? diagnosis.type ?? "").toLowerCase());
!result.valid &&
terminalTestStatuses.has(String(diagnosis.code ?? diagnosis.type ?? "").toLowerCase());
const testFailureCooldownMs = result.valid ? 0 : 30_000; // 30s retry window
const updateData: Record<string, any> = {

View File

@@ -0,0 +1,68 @@
import test from "node:test";
import assert from "node:assert/strict";
const { buildApiKeyConnectionTestResult } =
await import("../../src/app/api/providers/[id]/test/apiKeyTestResult.ts");
const FAILURE_DIAGNOSIS = {
type: "synthetic_failure",
source: "test",
message: "Synthetic validator failure",
code: "synthetic",
};
test("API-key connection tests preserve validator failure status codes", () => {
for (const statusCode of [401, 403, 429, 503]) {
const result = buildApiKeyConnectionTestResult(
{
valid: false,
warning: null,
statusCode,
},
`Synthetic validator failure ${statusCode}`,
FAILURE_DIAGNOSIS
);
assert.equal(result.valid, false);
assert.equal(result.statusCode, statusCode);
assert.deepEqual(result.diagnosis, FAILURE_DIAGNOSIS);
}
});
test("status-less semantic failures remain status-less", () => {
const result = buildApiKeyConnectionTestResult(
{
valid: false,
warning: null,
},
"Synthetic semantic failure",
FAILURE_DIAGNOSIS
);
assert.equal(result.valid, false);
assert.equal(result.statusCode, null);
assert.deepEqual(result.diagnosis, FAILURE_DIAGNOSIS);
});
test("successful validation does not synthesize an HTTP status", () => {
const diagnosis = {
type: "ok",
source: "upstream",
message: null,
code: null,
};
const result = buildApiKeyConnectionTestResult(
{
valid: true,
warning: null,
statusCode: 200,
},
null,
diagnosis
);
assert.equal(result.valid, true);
assert.equal(result.statusCode, null);
assert.deepEqual(result.diagnosis, diagnosis);
});