{"record":{"id":"75550f5f54ca8315","repo":"santifer/career-ops","slug":"http-resp-status","errorCode":null,"errorMessage":"HTTP ${resp.status}","messagePattern":"HTTP \\$\\{resp\\.status\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"openrouter-runner.mjs","lineNumber":123,"sourceCode":"// Models that failed permanently (403, timeout, persistent 429) — never retry\nconst blacklistedModels = new Set(loadPersistedBlacklist());\nif (blacklistedModels.size > 0) {\n  console.log(`[blacklist] Loaded ${blacklistedModels.size} pre-blacklisted model(s) from disk.`);\n}\n// 429 failure count per model — auto-blacklist after 3 consecutive 429s\nconst rateLimitCounts = {};\n\n// ---------------------------------------------------------------------------\n// Fetch free models from OpenRouter API\n// ---------------------------------------------------------------------------\nasync function loadFreeModels() {\n  if (freeModels !== null) return freeModels;\n\n  try {\n    const resp = await fetch(OPENROUTER_MODELS_URL, {\n      headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}` }\n    });\n    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n    const data = await resp.json();\n\n    // A model is free when its prompt and completion pricing are both \"0\"\n    const list = (data.data ?? [])\n      .filter(m => {\n        const p = m.pricing ?? {};\n        return String(p.prompt) === '0' && String(p.completion) === '0';\n      })\n      .map(m => m.id);\n\n    if (list.length === 0) throw new Error('No free models found in API response');\n\n    // Sort by provider priority; within the same provider sort alphabetically\n    function providerOf(id) { return id.split('/')[0]; }\n    function priorityOf(id) {\n      const idx = PROVIDER_PRIORITY.indexOf(providerOf(id));\n      return idx === -1 ? PROVIDER_PRIORITY.length : idx;\n    }","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/openrouter-runner.mjs#L105-L141","documentation":"Thrown by loadFreeModels() in openrouter-runner.mjs when the OpenRouter models endpoint (`OPENROUTER_MODELS_URL`) returns a non-ok HTTP status. The function caches the free-model list, so this only fires on the first fetch of the session (or after cache invalidation). The raw status code is surfaced verbatim so callers can distinguish auth problems (401/402) from server errors (5xx).","triggerScenarios":"First call that triggers loadFreeModels() when GET to the OpenRouter models API returns 401 (bad API key), 402 (credits/billing gate), 429 (rate limit), or 5xx from OpenRouter.","commonSituations":"Expired or revoked OPENROUTER_API_KEY in .env; typo'd key with stray whitespace; OpenRouter incident/maintenance returning 503; hitting rate limits after many rapid evaluations.","solutions":["Read the status: 401/402 means the key — verify OPENROUTER_API_KEY in .env matches a valid key at openrouter.ai/keys (no quotes/whitespace).","On 429, wait and retry; the list request is rate-limited per account.","On 5xx, check https://openrouter.statuspage.io or retry later — it is server-side.","Confirm network/proxy access to openrouter.ai (try `curl -H \"Authorization: Bearer $OPENROUTER_API_KEY\" https://openrouter.ai/api/v1/models`).","The wrapped catch at line 152 will rethrow with a hint (key vs no key) — use it to diagnose."],"exampleFix":"// before\nconst resp = await fetch(OPENROUTER_MODELS_URL, {\n  headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}` }\n});\nif (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n// after\nconst resp = await fetch(OPENROUTER_MODELS_URL, {\n  headers: { 'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}` }\n});\nif (!resp.ok) {\n  if (resp.status === 401 || resp.status === 402) throw new Error(`OpenRouter auth failed (${resp.status}) — check OPENROUTER_API_KEY`);\n  if (resp.status === 429) throw new Error('OpenRouter rate limit hit — retry shortly');\n  throw new Error(`OpenRouter models endpoint returned HTTP ${resp.status}`);\n}","handlingStrategy":"retry","validationCode":"const key = process.env.OPENROUTER_API_KEY;\nif (!key) throw new Error('OPENROUTER_API_KEY is not set');\nconst pre = await fetch('https://openrouter.ai/api/v1/models', { headers: { Authorization: `Bearer ${key}` } });\nif (!pre.ok) throw new Error(`OpenRouter models endpoint unhealthy: HTTP ${pre.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  models = await loadFreeModels();\n} catch (err) {\n  if (/HTTP 429/.test(err.message)) {\n    await sleep(30_000);\n    models = await loadFreeModels();\n  } else {\n    console.error('Model list load failed:', err.message);\n    process.exit(1);\n  }\n}","preventionTips":["Rotate/validate your OpenRouter key periodically from openrouter.ai/keys.","Retry 429/5xx with exponential backoff before failing.","Monitor https://openrouter.statuspage.io during batch runs.","Cache a known-good model list on disk to survive transient API failures."],"tags":["network","openrouter","http-error","api-key"],"backgroundTag":"http-error-response","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"}