{"record":{"id":"490b9dd08e0dd67a","repo":"musistudio/claude-code-router","slug":"openrouter-endpoints-request-failed-response-st","errorCode":null,"errorMessage":"OpenRouter endpoints request failed (${response.status}): ${text.slice(0, 200)}","messagePattern":"OpenRouter endpoints request failed \\((.+?)\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/plugins/built-ins/openrouter-discount-provider-router.ts","lineNumber":369,"sourceCode":"    controller.abort(new Error(`OpenRouter endpoints request timed out after ${endpointFetchTimeoutMs}ms`));\n  }, endpointFetchTimeoutMs);\n  timer.unref?.();\n\n  let response: Response;\n  try {\n    response = await fetch(url, { signal: controller.signal });\n  } catch (error) {\n    if (controller.signal.aborted) {\n      throw new Error(`OpenRouter endpoints request timed out after ${endpointFetchTimeoutMs}ms`);\n    }\n    throw error;\n  } finally {\n    clearTimeout(timer);\n  }\n\n  if (!response.ok) {\n    const text = await response.text().catch(() => \"\");\n    throw new Error(`OpenRouter endpoints request failed (${response.status}): ${text.slice(0, 200)}`);\n  }\n\n  const payload = await response.json() as unknown;\n  const record = isRecord(payload) ? payload : {};\n  const data = isRecord(record.data) ? record.data : {};\n  const endpoints = Array.isArray(data.endpoints)\n    ? data.endpoints\n    : Array.isArray(record.endpoints)\n      ? record.endpoints\n      : [];\n  return endpoints;\n}\n\nfunction pruneEndpointCache(): void {\n  if (endpointCache.size <= endpointCacheMaxEntries) {\n    return;\n  }\n  const overflow = endpointCache.size - endpointCacheMaxEntries;","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/plugins/built-ins/openrouter-discount-provider-router.ts#L351-L387","documentation":"A non-2xx response from OpenRouter's model endpoints API surfaces as this error, including the HTTP status and first 200 chars of the body for diagnosis. The failure is then cached to trigger the cooldown for subsequent calls.","triggerScenarios":"HTTP 401/403 (bad API key), 404 (unknown author/slug), 429 (rate limit), or 5xx from OpenRouter.","commonSituations":"Expired or missing OPENROUTER_API_KEY, removed/deprecated model ids, hitting free-tier rate limits.","solutions":["Check the embedded status: 401/403 -> fix the API key; 404 -> verify the model id exists; 429 -> back off per the cooldown","Inspect the body snippet in the message for OpenRouter's error detail","Confirm the apiRoot points at the intended OpenRouter deployment"],"exampleFix":"// before\napiKey: \"\"\n// after\napiKey: process.env.OPENROUTER_API_KEY","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"const isOpenRouterHttpError = (e: unknown): boolean => e instanceof Error && /OpenRouter endpoints request failed \\((\\d+)\\)/.test(e.message);","tryCatchPattern":"try { return await loadModelEndpoints(model); } catch (e) { const m = /request failed \\((\\d+)/.exec(String((e as Error).message)); if (m) { const status = Number(m[1]); if (status === 401 || status === 403) throw new Error(\"check OPENROUTER_API_KEY\"); if (status === 429) await sleep(cooldown); } throw e; }","preventionTips":["Monitor status codes in logs","Keep API keys valid and rotate before expiry"],"tags":["openrouter","http-error","api"],"backgroundTag":"http-error-response","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}