{"record":{"id":"952cdcf04f8c67a1","repo":"farion1231/cc-switch","slug":"http-response-status","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/modelsDevPricing.ts","lineNumber":152,"sourceCode":"    (a, b) =>\n      b.releaseDate.localeCompare(a.releaseDate) ||\n      a.modelName.localeCompare(b.modelName),\n  );\n  return entries;\n}\n\nexport async function fetchModelsDevPricing(): Promise<ModelsDevResponse> {\n  const controller = new AbortController();\n  const timeout = window.setTimeout(\n    () => controller.abort(),\n    MODELS_DEV_FETCH_TIMEOUT_MS,\n  );\n  try {\n    const response = await fetch(MODELS_DEV_API_URL, {\n      signal: controller.signal,\n    });\n    if (!response.ok) {\n      throw new Error(`HTTP ${response.status}`);\n    }\n    return (await response.json()) as ModelsDevResponse;\n  } finally {\n    window.clearTimeout(timeout);\n  }\n}\n\nconst COMMON_MODEL_LIMIT_PER_FAMILY = 6;\n\ninterface CommonFamilyRule {\n  id: string;\n  providers: ReadonlySet<string>;\n  matches: (modelId: string) => boolean;\n}\n\nconst COMMON_FAMILY_RULES: CommonFamilyRule[] = [\n  {\n    id: \"claude\",","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/farion1231/cc-switch/blob/a2e22f330273a5b6ffa87cb8b82b624601bac562/src/lib/modelsDevPricing.ts#L134-L170","documentation":"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.","triggerScenarios":"fetch(MODELS_DEV_API_URL) returning 404/429/500/502: upstream outage, CDN error page, corporate proxy answering 403, rate limiting.","commonSituations":"Refreshing model pricing behind a corporate proxy; transient 5xx during upstream deploys; polling too aggressively and hitting 429.","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"],"exampleFix":"// before\nconst pricing = await fetchModelsDevPricing(); // throws 'HTTP 503' on outage\n\n// after\nasync function fetchPricingWithRetry(retries = 3): Promise<ModelsDevResponse> {\n  for (let i = 0; ; i++) {\n    try {\n      return await fetchModelsDevPricing();\n    } catch (e) {\n      const retryable = e instanceof Error && /^HTTP (429|5\\d{2})$/.test(e.message);\n      if (!retryable || i === retries - 1) throw e;\n      await new Promise((r) => setTimeout(r, 500 * 2 ** i));\n    }\n  }\n}","handlingStrategy":"retry","validationCode":"// Cheap pre-flight for the common offline case\nif (typeof navigator !== \"undefined\" && !navigator.onLine) {\n  return useCachedPricing(); // skip the doomed fetch\n}\nreturn await fetchModelsDevPricing();","typeGuard":"function isModelsDevHttpError(e: unknown): e is Error {\n  return e instanceof Error && /^HTTP \\d{3}$/.test(e.message);\n}","tryCatchPattern":"let lastError: unknown;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    return await fetchModelsDevPricing();\n  } catch (e) {\n    lastError = e;\n    const status = isModelsDevHttpError(e) ? Number(e.message.slice(5)) : 0;\n    const retryable = status === 429 || status >= 500;\n    if (!retryable) throw e;\n    await new Promise((r) => setTimeout(r, 500 * 2 ** attempt));\n  }\n}\nthrow lastError;","preventionTips":["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"],"tags":["network","http","pricing","api","retry"],"backgroundTag":null,"analyzedSha":"a2e22f330273a5b6ffa87cb8b82b624601bac562","analyzedAt":"2026-08-16T03:46:07.889Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}