santifer/career-ops · error · Error

[models] Failed to fetch free model list

Error message

[models] Failed to fetch free model list: ${reason}. Check that your API key is valid and that network access to OpenRouter is available.

What it means

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.

Solutions

  1. Reproduce with the helper: `node openrouter-runner.mjs models` — same code path, clearer output
  2. 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
  3. Verify egress: curl https://openrouter.ai from the same shell/container; check HTTP(S)_PROXY env if a proxy intercepts
  4. If the prefixed reason says 'No free models found in API response', retry later — the catalog momentarily exposed no zero-priced models
  5. For 5xx reasons, wait and retry; nothing is wrong locally
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: key present AND models endpoint reachable before starting a run
async function preflightOpenRouter() {
  const key = process.env.OPENROUTER_API_KEY;
  if (!key) throw new Error('OPENROUTER_API_KEY missing — see .env setup');
  const r = await fetch('https://openrouter.ai/api/v1/models', {
    headers: { Authorization: `Bearer ${key}` },
  });
  if (!r.ok) throw new Error(`OpenRouter preflight failed: HTTP ${r.status}`);
  const { data } = await r.json();
  const free = (data ?? []).filter(m => String(m.pricing?.prompt) === '0' && String(m.pricing?.completion) === '0');
  if (free.length === 0) throw new Error('No free models in catalog right now');
  return free.map(m => m.id);
}

Try / catch

try {
  const models = await loadFreeModels();
} catch (e) {
  if (e.message.startsWith('[models] Failed to fetch free model list')) {
    // branch on the suffix: key-variant vs network-variant
    console.error(e.message.includes('OPENROUTER_API_KEY is not set')
      ? 'Fix .env first (cp .env.example .env)'
      : 'OpenRouter unreachable or key rejected — check network/key');
  }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/da337590ce05ab7e. Report an issue: GitHub.

Appendix: source

Thrown at openrouter-runner.mjs:140

    if (list.length === 0) throw new Error('No free models found in API response');

    // Sort by provider priority; within the same provider sort alphabetically
    function providerOf(id) { return id.split('/')[0]; }
    function priorityOf(id) {
      const idx = PROVIDER_PRIORITY.indexOf(providerOf(id));
      return idx === -1 ? PROVIDER_PRIORITY.length : idx;
    }

    freeModels = list.sort((a, b) => {
      const diff = priorityOf(a) - priorityOf(b);
      return diff !== 0 ? diff : a.localeCompare(b);
    });

    console.log(`[models] ${freeModels.length} free models loaded from OpenRouter API.`);
  } catch (e) {
    const reason = e instanceof Error ? e.message : String(e);
    const hasKey = Boolean(process.env.OPENROUTER_API_KEY);
    throw new Error(
      `[models] Failed to fetch free model list: ${reason}. ` +
      (hasKey ? 'Check that your API key is valid and that network access to OpenRouter is available.'
               : 'OPENROUTER_API_KEY is not set — copy .env.example to .env and add your key.')
    );
  }

  return freeModels;
}

// List and exit (helper command)
async function cmdModels() {
  const models = await loadFreeModels();
  console.log(`\nFree models available on OpenRouter (${models.length} total):\n`);
  models.forEach((id, i) => console.log(`  ${String(i + 1).padStart(2)}. ${id}`));
  console.log('');
}

// ---------------------------------------------------------------------------

View on GitHub (pinned to 60398d6549)