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

  1. Retry with exponential backoff for 429/5xx (honor Retry-After when present)
  2. Fall back to the last cached pricing snapshot when the fetch fails
  3. 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

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.