{"record":{"id":"a72a9a280b591522","repo":"santifer/career-ops","slug":"data-error-message","errorCode":null,"errorMessage":"${data.error.message}","messagePattern":"\\$\\{data\\.error\\.message\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"openrouter-runner.mjs","lineNumber":262,"sourceCode":"    const timerId = setTimeout(() => ctrl.abort(), MODEL_TIMEOUT_MS);\n    try {\n      const resp = await fetch(OPENROUTER_API_URL, {\n        method: 'POST',\n        headers: {\n          'Authorization': `Bearer ${key}`,\n          'Content-Type':  'application/json',\n          'HTTP-Referer':  'https://github.com/career-ops-hq/career-ops',\n          'X-Title':       'career-ops',\n        },\n        body,\n        signal: ctrl.signal,\n      });\n      if (!resp.ok) {\n        const t = await resp.text();\n        throw new Error(`HTTP ${resp.status}: ${t.slice(0, 120)}`);\n      }\n      const data = await resp.json();\n      if (data.error) throw new Error(data.error.message);\n      const content = data.choices?.[0]?.message?.content ?? '';\n      if (!content) throw new Error('Empty response');\n      console.log('OK');\n      const usage = normalizeOpenAIUsage(data.usage);\n      return { content, usage };\n    } catch (e) {\n      if (e.name === 'AbortError') throw new Error(`Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s`);\n      throw e;\n    } finally {\n      clearTimeout(timerId);\n    }\n  }\n\n  const models = await loadFreeModels();\n  let lastError;\n\n  if (models.length === 0) {\n    throw new Error(","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/openrouter-runner.mjs#L244-L280","documentation":"Thrown when the completions endpoint returns HTTP 200 but the JSON body contains an `error` object — OpenRouter's way of reporting provider-level failures (upstream model errors, moderation, context-length overflow) with a 200 envelope. The library surfaces `data.error.message` verbatim so the OpenRouter-provided explanation is what the developer sees.","triggerScenarios":"Response is 200 OK, resp.json() succeeds, and `data.error` is truthy — e.g. upstream provider timed out, the model refused/errored, prompt exceeded the model's context window, or content moderation rejected the request.","commonSituations":"Free-tier model's upstream provider returning errors under load; prompts longer than the model's context limit; provider deprecating a model but still accepting requests that fail downstream.","solutions":["Read data.error.message in the thrown message — it identifies the upstream cause.","If it is a context-length error, shorten the prompt or pick a model with a larger window.","If the upstream model is failing, retry — the rotation should blacklist it and try another free model.","If moderation-related, rephrase the prompt.","Treat 200-with-error as a failure signal in any monitoring; don't count it as success because the HTTP status was OK."],"exampleFix":"// before\nif (data.error) throw new Error(data.error.message);\n// after\nif (data.error) {\n  const err = new Error(data.error.message ?? JSON.stringify(data.error));\n  err.code = data.error.code;\n  err.upstream = true;   // rotation logic can blacklist this model\n  throw err;\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"function hasUpstreamError(data) {\n  return data != null && typeof data === 'object' && data.error != null;\n}","tryCatchPattern":"try {\n  result = await callOpenRouter(systemPrompt, userMessage);\n} catch (err) {\n  // rotation already blacklists the model; fall back to a known-good model\n  result = await callWithFallbackModel(systemPrompt, userMessage);\n}","preventionTips":["Never treat HTTP 200 as success without checking data.error.","Keep prompt sizes under the smallest free model's context window.","Fall back to another model on upstream errors instead of failing the whole run.","Track which models emit upstream errors most and deprioritize them."],"tags":["openrouter","api","upstream-error","model-rotation"],"backgroundTag":"upstream-api-error","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}