{"record":{"id":"dc5567bd917bd37b","repo":"thedotmack/claude-mem","slug":"openrouter-api-error-response-status-errortext","errorCode":null,"errorMessage":"OpenRouter API error: ${response.status} - ${errorText}","messagePattern":"OpenRouter API error: (.+?) - (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/worker/OpenRouterProvider.ts","lineNumber":554,"sourceCode":"    const data = await withRetry<OpenRouterResponse>(async (attemptSignal) => {\n      let response: Response;\n      try {\n        response = await this.fetchChatCompletion(apiUrl, apiKey, model, fallbackModels, messages, siteUrl, appName, priorRequestId, attemptSignal, plainText);\n      } catch (networkError: unknown) {\n        const err = networkError instanceof Error ? networkError : new Error(String(networkError));\n        throw classifyOpenRouterError({ cause: err });\n      }\n\n      const requestId = response.headers.get('x-request-id') ?? response.headers.get('x-openrouter-request-id');\n      if (requestId) {\n        priorRequestId = requestId;\n      } else {\n        logger.debug('SDK', 'OpenRouter response missing request-id header; retry dedup is best-effort');\n      }\n\n      if (!response.ok) {\n        const errorText = await response.text();\n        throw classifyOpenRouterError({\n          status: response.status,\n          bodyText: errorText,\n          headers: response.headers,\n          cause: new Error(`OpenRouter API error: ${response.status} - ${errorText}`),\n          ...(requestId ? { requestId } : {}),\n        });\n      }\n\n      const responseData = await response.json() as OpenRouterResponse;\n\n      if (responseData.error) {\n        // Per OpenRouter spec, errors can come in 200 responses too.\n        throw classifyOpenRouterError({\n          status: response.status,\n          bodyText: JSON.stringify(responseData),\n          headers: response.headers,\n          cause: new Error(`OpenRouter API error: ${responseData.error.code} - ${responseData.error.message}`),\n          ...(requestId ? { requestId } : {}),","sourceCodeStart":536,"sourceCodeEnd":572,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/services/worker/OpenRouterProvider.ts#L536-L572","documentation":"Thrown in queryOpenRouterMultiTurn when the OpenRouter HTTP response has a non-OK status. The raw status code and response body are wrapped as the cause and passed to classifyOpenRouterError, which maps it to a typed error (rate limit, auth, upstream provider, etc.). It signals the chat-completion request failed at the HTTP level before any model output was produced.","triggerScenarios":"Any OpenRouter /chat/completions request where response.ok is false: 401 invalid API key, 402 insufficient credits, 429 rate limited, 5xx from OpenRouter or an upstream model provider, 400 malformed request body.","commonSituations":"Expired or missing OPENROUTER_API_KEY, exhausted account credits, model slug typo (e.g. wrong vendor prefix), free-tier rate limits hit, upstream provider outage for the chosen model.","solutions":["Read the classified error (status/body) to identify the HTTP cause; fix auth or billing first for 401/402","Retry with backoff on 429/5xx; the code already captures request-id headers for retry dedup","Verify the model slug exists on OpenRouter (GET /api/v1/models)","Log errorText from the cause for the upstream provider's detailed message"],"exampleFix":"// before\nconst res = await fetch(OPENROUTER_URL, { headers: { Authorization: `Bearer ${key}` } });\n// after\nif (!key?.startsWith('sk-or-')) throw new Error('OPENROUTER_API_KEY missing/invalid before call');\nconst res = await fetch(OPENROUTER_URL, { headers: { Authorization: `Bearer ${key}` } });\nif (res.status === 429) await backoffRetry(() => callOpenRouter(messages));","handlingStrategy":"retry","validationCode":"if (!process.env.OPENROUTER_API_KEY) throw new Error('OPENROUTER_API_KEY not set');\n// model slug sanity\nif (!/^[-a-zA-Z0-9/_.]+$/.test(model)) throw new Error(`suspicious model slug: ${model}`);","typeGuard":"function isRateLimitOrServer(e: unknown): e is { status: number } {\n  return typeof e === 'object' && e !== null && 'status' in e && typeof (e as any).status === 'number' && ((e as any).status === 429 || (e as any).status >= 500);\n}","tryCatchPattern":"try {\n  return await provider.query(messages);\n} catch (e) {\n  if (isRetryable(e)) { await sleep(backoff()); return await provider.query(messages); }\n  throw e;\n}","preventionTips":["Validate the API key and account credits before deployment","Pin and verify model slugs against GET /api/v1/models","Implement exponential backoff with jitter for 429/5xx","Log status + body for every failed call to speed diagnosis"],"tags":["api","http","openrouter","network"],"backgroundTag":"http-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"}