farion1231/cc-switch · error · Error
HTTP ${response.status}
Error message
HTTP ${response.status} What it means
Thrown by fetchModelsDevPricing when the models.dev pricing endpoint answers with a non-2xx status. The fetch itself reached the server (the AbortController timeout raises a separate AbortError, not this), so 'HTTP <status>' strictly means the server/proxy responded with an error status code and no pricing JSON is available.
Source
Thrown at src/lib/modelsDevPricing.ts:152
(a, b) =>
b.releaseDate.localeCompare(a.releaseDate) ||
a.modelName.localeCompare(b.modelName),
);
return entries;
}
export async function fetchModelsDevPricing(): Promise<ModelsDevResponse> {
const controller = new AbortController();
const timeout = window.setTimeout(
() => controller.abort(),
MODELS_DEV_FETCH_TIMEOUT_MS,
);
try {
const response = await fetch(MODELS_DEV_API_URL, {
signal: controller.signal,
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return (await response.json()) as ModelsDevResponse;
} finally {
window.clearTimeout(timeout);
}
}
const COMMON_MODEL_LIMIT_PER_FAMILY = 6;
interface CommonFamilyRule {
id: string;
providers: ReadonlySet<string>;
matches: (modelId: string) => boolean;
}
const COMMON_FAMILY_RULES: CommonFamilyRule[] = [
{
id: "claude",View on GitHub (pinned to a2e22f3302)
Solutions
- Retry with exponential backoff for 429/5xx (honor Retry-After when present)
- Fall back to the last cached pricing snapshot when the fetch fails
- Inspect the status before parsing; escalate non-retryable 4xx immediately
Example fix
// before
const pricing = await fetchModelsDevPricing(); // throws 'HTTP 503' on outage
// after
async function fetchPricingWithRetry(retries = 3): Promise<ModelsDevResponse> {
for (let i = 0; ; i++) {
try {
return await fetchModelsDevPricing();
} catch (e) {
const retryable = e instanceof Error && /^HTTP (429|5\d{2})$/.test(e.message);
if (!retryable || i === retries - 1) throw e;
await new Promise((r) => setTimeout(r, 500 * 2 ** i));
}
}
} Defensive patterns
Strategy: retry
Validate before calling
// Cheap pre-flight for the common offline case
if (typeof navigator !== "undefined" && !navigator.onLine) {
return useCachedPricing(); // skip the doomed fetch
}
return await fetchModelsDevPricing(); Type guard
function isModelsDevHttpError(e: unknown): e is Error {
return e instanceof Error && /^HTTP \d{3}$/.test(e.message);
} Try / catch
let lastError: unknown;
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await fetchModelsDevPricing();
} catch (e) {
lastError = e;
const status = isModelsDevHttpError(e) ? Number(e.message.slice(5)) : 0;
const retryable = status === 429 || status >= 500;
if (!retryable) throw e;
await new Promise((r) => setTimeout(r, 500 * 2 ** attempt));
}
}
throw lastError; Prevention
- Cache the last good pricing response with a TTL and serve it on failure
- Honor Retry-After when a 429 carries it
- Distinguish AbortError (timeout) from HTTP status errors in error handling
When it happens
Trigger: fetch(MODELS_DEV_API_URL) returning 404/429/500/502: upstream outage, CDN error page, corporate proxy answering 403, rate limiting.
Common situations: Refreshing model pricing behind a corporate proxy; transient 5xx during upstream deploys; polling too aggressively and hitting 429.
Related errors
AI-assisted analysis of farion1231/cc-switch@a2e22f3302 (2026-08-16).
Data as JSON: /api/errors/952cdcf04f8c67a1.
Report an issue: GitHub.