{"record":{"id":"fb6adacb15c40038","repo":"musistudio/claude-code-router","slug":"openrouter-endpoints-request-timed-out-after-end","errorCode":null,"errorMessage":"OpenRouter endpoints request timed out after ${endpointFetchTimeoutMs}ms","messagePattern":"OpenRouter endpoints request timed out after (.+?)ms","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/core/src/plugins/built-ins/openrouter-discount-provider-router.ts","lineNumber":360,"sourceCode":"    endpointInflight.delete(cacheKey);\n  });\n  endpointInflight.set(cacheKey, promise);\n  return promise;\n}\n\nasync function fetchModelEndpoints(url: string): Promise<unknown[]> {\n  const controller = new AbortController();\n  const timer = setTimeout(() => {\n    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","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/plugins/built-ins/openrouter-discount-provider-router.ts#L342-L378","documentation":"fetchModelEndpoints wraps fetch with an AbortController timeout of endpointFetchTimeoutMs; if the abort fires, the caught error is converted into this explicit timeout message (distinguishing it from genuine network errors).","triggerScenarios":"OpenRouter's /endpoints endpoint taking longer than the configured timeout; slow proxies; transient network stalls.","commonSituations":"Low timeout defaults in restrictive networks, corporate proxies adding latency, or OpenRouter degradation.","solutions":["Retry after the cooldown (the failure is cached) — transient timeouts often clear","Increase endpointFetchTimeoutMs if your network is consistently slow","Check proxy/VPN latency to openrouter.ai"],"exampleFix":"// before\n{ endpointFetchTimeoutMs: 2000 }\n// after\n{ endpointFetchTimeoutMs: 15000 }","handlingStrategy":"retry","validationCode":"null","typeGuard":"const isEndpointTimeout = (e: unknown): boolean => e instanceof Error && e.message.includes(\"timed out after\");","tryCatchPattern":"try { return await loadModelEndpoints(model); } catch (e) { if (isEndpointTimeout(e)) return await withBackoff(() => loadModelEndpoints(model), 3); throw e; }","preventionTips":["Tune endpointFetchTimeoutMs to your network","Wrap calls in exponential backoff with cooldown awareness"],"tags":["openrouter","timeout","network"],"backgroundTag":"request-timeout","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}