farion1231/cc-switch · error · Error
HTTP
Error message
HTTP ${response.status} What it means
fetchModelsDevPricing fetches the models.dev pricing catalog with an AbortController-bounded timeout (MODELS_DEV_FETCH_TIMEOUT_MS) and throws 'HTTP <status>' whenever response.ok is false. The message intentionally carries only the status code; a timeout instead surfaces as an AbortError from fetch, not as this error. This is a transient server-side failure class: 5xx outages and 429 rate limits are the usual statuses.
Solutions
- Retry with exponential backoff (especially for 429/5xx) before giving up
- Cache the last successful ModelsDevResponse and serve it when the fetch fails
- Degrade gracefully — pricing is auxiliary data, so keep provider switching working without it
Example fix
// before
const data = await fetchModelsDevPricing(); // throws 'HTTP 503'
// after
let data: ModelsDevResponse | null = null;
for (let attempt = 0; attempt < 3 && !data; attempt++) {
try {
data = await fetchModelsDevPricing();
} catch (error) {
if (attempt === 2) {
data = lastGoodCache; // fall back to last good copy
} else {
await delay(2 ** attempt * 500);
}
}
} Defensive patterns
Strategy: retry
Try / catch
async function fetchPricingSafe(fallback: ModelsDevResponse | null): Promise<ModelsDevResponse | null> {
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await fetchModelsDevPricing();
} catch (error) {
const msg = error instanceof Error ? error.message : '';
const retriable = msg.startsWith('HTTP 5') || msg === 'HTTP 429';
if (!retriable || attempt === 2) return fallback; // degrade to cache/offline
await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
}
}
return fallback;
} Prevention
- Cache the last good pricing payload with a timestamp
- Treat pricing as optional data so the app works offline
- Only retry 429/5xx; fail fast on other statuses
When it happens
Trigger: fetch(MODELS_DEV_API_URL) resolves with response.ok === false — e.g. models.dev returning 502/503 behind its CDN, or 429 when the client retries too aggressively.
Common situations: App startup while models.dev is degraded; CI/e2e runs hitting rate limits; corporate proxies returning 403 interstitial pages.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
AI-assisted analysis of farion1231/cc-switch@0b5da51016 (2026-08-20).
Data as JSON: /api/errors/952cdcf04f8c67a1.
Report an issue: GitHub.
Appendix: 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 0b5da51016)