{"record":{"id":"da337590ce05ab7e","repo":"santifer/career-ops","slug":"models-failed-to-fetch-free-model-list-reason","errorCode":null,"errorMessage":"[models] Failed to fetch free model list: ${reason}. Check that your API key is valid and that network access to OpenRouter is available.","messagePattern":"\\[models\\] Failed to fetch free model list: (.+?)\\. Check that your API key is valid and that network access to OpenRouter is available\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"openrouter-runner.mjs","lineNumber":140,"sourceCode":"    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    }\n\n    freeModels = list.sort((a, b) => {\n      const diff = priorityOf(a) - priorityOf(b);\n      return diff !== 0 ? diff : a.localeCompare(b);\n    });\n\n    console.log(`[models] ${freeModels.length} free models loaded from OpenRouter API.`);\n  } catch (e) {\n    const reason = e instanceof Error ? e.message : String(e);\n    const hasKey = Boolean(process.env.OPENROUTER_API_KEY);\n    throw new Error(\n      `[models] Failed to fetch free model list: ${reason}. ` +\n      (hasKey ? 'Check that your API key is valid and that network access to OpenRouter is available.'\n               : 'OPENROUTER_API_KEY is not set — copy .env.example to .env and add your key.')\n    );\n  }\n\n  return freeModels;\n}\n\n// List and exit (helper command)\nasync function cmdModels() {\n  const models = await loadFreeModels();\n  console.log(`\\nFree models available on OpenRouter (${models.length} total):\\n`);\n  models.forEach((id, i) => console.log(`  ${String(i + 1).padStart(2)}. ${id}`));\n  console.log('');\n}\n\n// ---------------------------------------------------------------------------","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/openrouter-runner.mjs#L122-L158","documentation":"loadFreeModels() in openrouter-runner.mjs GETs https://openrouter.ai/api/v1/models with an Authorization: Bearer header and needs at least one model whose pricing.prompt AND pricing.completion are both the string \"0\". This variant of the wrapper (openrouter-runner.mjs:140) fires when OPENROUTER_API_KEY IS set but something in that fetch/filter threw: the original reason is prefixed to the message — 'HTTP 401' (invalid/revoked key), 'HTTP 429'/'HTTP 5xx' (provider side), a network/DNS TypeError from fetch, or 'No free models found in API response' when the catalog exposed zero zero-priced models.","triggerScenarios":"node openrouter-runner.mjs <mode> (or the `models` helper command) with a key that is malformed, revoked, or has no access; egress to openrouter.ai blocked by firewall/proxy; OpenRouter outage; a catalog snapshot where every model has non-zero pricing (filter at line 116-119 empties and line 122 throws).","commonSituations":"Key revoked/rotated on openrouter.ai after it leaked into a dotfile; corporate proxy blocking openrouter.ai; OpenRouter briefly deprecating all :free variants; DNS failure in CI containers.","solutions":["Reproduce with the helper: `node openrouter-runner.mjs models` — same code path, clearer output","Check the key: curl -s -H \"Authorization: Bearer $OPENROUTER_API_KEY\" https://openrouter.ai/api/v1/models -o /dev/null -w '%{http_code}' — 200 means key fine, look at network next; 401/403 means the key is bad, create a new one at openrouter.ai/keys","Verify egress: curl https://openrouter.ai from the same shell/container; check HTTP(S)_PROXY env if a proxy intercepts","If the prefixed reason says 'No free models found in API response', retry later — the catalog momentarily exposed no zero-priced models","For 5xx reasons, wait and retry; nothing is wrong locally"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-flight: key present AND models endpoint reachable before starting a run\nasync function preflightOpenRouter() {\n  const key = process.env.OPENROUTER_API_KEY;\n  if (!key) throw new Error('OPENROUTER_API_KEY missing — see .env setup');\n  const r = await fetch('https://openrouter.ai/api/v1/models', {\n    headers: { Authorization: `Bearer ${key}` },\n  });\n  if (!r.ok) throw new Error(`OpenRouter preflight failed: HTTP ${r.status}`);\n  const { data } = await r.json();\n  const free = (data ?? []).filter(m => String(m.pricing?.prompt) === '0' && String(m.pricing?.completion) === '0');\n  if (free.length === 0) throw new Error('No free models in catalog right now');\n  return free.map(m => m.id);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const models = await loadFreeModels();\n} catch (e) {\n  if (e.message.startsWith('[models] Failed to fetch free model list')) {\n    // branch on the suffix: key-variant vs network-variant\n    console.error(e.message.includes('OPENROUTER_API_KEY is not set')\n      ? 'Fix .env first (cp .env.example .env)'\n      : 'OpenRouter unreachable or key rejected — check network/key');\n  }\n}","preventionTips":["Run `node openrouter-runner.mjs models` once after any key rotation to confirm auth works","Keep the OpenRouter key in the repo-root .env only; rotate it if it ever appears in logs","In CI, add the preflight check as a separate step so failures are labeled auth vs network clearly"],"tags":["openrouter","llm","api","auth","network"],"backgroundTag":"invalid-api-key","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":"2026-08-20T23:00:06.764Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}