{"record":{"id":"addcb9d58aebfc0a","repo":"thedotmack/claude-mem","slug":"openrouter-api-error-response-status-bodytext","errorCode":null,"errorMessage":"OpenRouter API error: ${response.status} - ${bodyText}","messagePattern":"OpenRouter API error: (.+?) - (.+?)","errorType":"exception","errorClass":"ServerClassifiedProviderError","httpStatus":null,"severity":"error","filePath":"src/server/generation/providers/OpenRouterObservationProvider.ts","lineNumber":96,"sourceCode":"        providerLabel: this.providerLabel,\n        modelId: this.model,\n      };\n    }\n\n    let response: Response;\n    try {\n      response = await this.postChatCompletion(prompt, signal);\n    } catch (networkError) {\n      const err = networkError instanceof Error ? networkError : new Error(String(networkError));\n      throw classifyHttpProviderError({\n        cause: err,\n        providerLabel: 'OpenRouter',\n      });\n    }\n\n    if (!response.ok) {\n      const bodyText = await safeReadBody(response);\n      throw classifyHttpProviderError({\n        status: response.status,\n        bodyText,\n        headers: response.headers,\n        cause: new Error(`OpenRouter API error: ${response.status} - ${bodyText}`),\n        providerLabel: 'OpenRouter',\n      });\n    }\n\n    let data: OpenRouterResponse;\n    try {\n      data = (await response.json()) as OpenRouterResponse;\n    } catch (parseError) {\n      const err = parseError instanceof Error ? parseError : new Error(String(parseError));\n      throw new ServerClassifiedProviderError('OpenRouter returned invalid JSON', {\n        kind: 'parse_error',\n        cause: err,\n      });\n    }","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/server/generation/providers/OpenRouterObservationProvider.ts#L78-L114","documentation":"OpenRouter returned a non-2xx HTTP status to the chat-completions request. The provider reads the body and throws classifyHttpProviderError with status, bodyText, headers, cause `OpenRouter API error: <status> - <body>`, and label 'OpenRouter'. The classifier maps quota bodies, 429 (with Retry-After), and 5xx to retryable kinds and other 4xx to unrecoverable.","triggerScenarios":"`response.ok` is false after postChatCompletion: 401 missing/invalid OPENROUTER_API_KEY, 402 insufficient credits, 429 rate limit, 400 bad model slug, 5xx upstream model errors — thrown at OpenRouterObservationProvider.ts:93-100.","commonSituations":"Out of OpenRouter credits (402), revoked or missing API key, invalid model slug (model not found on OpenRouter), rate limiting from parallel requests, upstream provider outages surfaced as 502/503.","solutions":["Read the classified kind: retryable → retry with retryAfterMs; unrecoverable → fix credentials/model/payload.","Top up OpenRouter credits if status is 402.","Verify OPENROUTER_API_KEY is set and valid; check the model slug exists on openrouter.ai/models.","Honor Retry-After on 429 and serialize/throttle parallel generation requests."],"exampleFix":"// before\nmodel = 'anthropic/claude-v2';\n// after\nmodel = 'anthropic/claude-sonnet-4'; // valid OpenRouter slug\nif (!process.env.OPENROUTER_API_KEY) throw new Error('OPENROUTER_API_KEY missing');","handlingStrategy":"retry","validationCode":"if (!process.env.OPENROUTER_API_KEY) throw new Error('OPENROUTER_API_KEY not set');\n// validate the model slug is available: GET https://openrouter.ai/api/v1/models","typeGuard":"function isRetryableProviderError(e: unknown): boolean {\n  return typeof e === 'object' && e !== null && 'kind' in e && (e as {kind: string}).kind === 'retryable';\n}","tryCatchPattern":"try {\n  result = await openRouterProvider.generate(prompt, signal);\n} catch (e) {\n  if (e.status === 402) throw new Error('OpenRouter credits exhausted — top up at openrouter.ai');\n  if (e.kind === 'retryable' && e.retryAfterMs) return sleep(e.retryAfterMs).then(call);\n  throw e;\n}","preventionTips":["Monitor credit balance; alert before 402s occur.","Validate model slugs against the OpenRouter models list.","Throttle concurrent requests to avoid 429s; honor Retry-After.","Configure a fallback model/provider for upstream 5xx outages."],"tags":["openrouter","http","api-error","credits","rate-limit"],"backgroundTag":"api-error-response","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}