santifer/career-ops · error · Error
No free OpenRouter models are available. Model loading may…
Error message
No free OpenRouter models are available. Model loading may have failed, or your account currently has no free models.
What it means
Thrown by callOpenRouter() after loadFreeModels() returns an empty array — meaning the model list loaded 'successfully' but contains nothing, so there is no model to call. Distinct from error 92 (which fires inside loadFreeModels when filtering finds nothing): this fires when an empty list reached the caller anyway, e.g. a cached empty list or a future path where loadFreeModels returns [] without throwing.
Solutions
- Clear the module-level cache (restart the process) so loadFreeModels refetches from the API.
- Run a standalone fetch of the models endpoint to check whether free models actually exist right now.
- Check the earlier `[models] Failed to fetch...` log lines — an earlier failure may have poisoned the cache.
- If no free models exist for the account/plan, configure paid models and ensure the key has credits.
- Harden loadFreeModels so it never caches an empty list as a success value.
Example fix
// before const freeModels = ...; if (freeModels !== null) return freeModels; // after (don't cache empty as success) const freeModels = ...; if (freeModels !== null && freeModels.length > 0) return freeModels;
Defensive patterns
Strategy: validation
Validate before calling
const models = await loadFreeModels();
if (!Array.isArray(models) || models.length === 0) {
throw new Error('No free models available — restart to clear the cache or add credits/paid models.');
} Type guard
function hasUsableModels(models) {
return Array.isArray(models) && models.some(m => typeof m === 'string' && m.length > 0);
} Try / catch
try {
result = await callOpenRouter(systemPrompt, userMessage);
} catch (err) {
if (err.message.startsWith('No free OpenRouter models')) {
console.error('Model list empty: restart the process to force a refetch, or switch to paid models.');
process.exit(1);
}
throw err;
} Prevention
- Never cache an empty model list as a success value.
- Refresh the model list at the start of each session rather than trusting session cache.
- Alert when loadFreeModels() returns fewer than a threshold (e.g. 3) of free models.
- Maintain a configured paid-model fallback for accounts without free models.
When it happens
Trigger: `await loadFreeModels()` resolves to [] — a previously cached empty result, or a code path where the fetch failure was swallowed and [] returned; callOpenRouter then aborts before building the rotation list.
Common situations: Session-level cache holding an empty list from a failed first load; test/mocked environment returning []; OpenRouter genuinely listing no free models for the account at load time.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- ${data.error.message}
- Empty response
- HTTP
- No free models found in API response
- Pinned model timed out after
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/1a187bb0a9d29d91.
Report an issue: GitHub.
Appendix: source
Thrown at openrouter-runner.mjs:280
if (data.error) throw new Error(data.error.message);
const content = data.choices?.[0]?.message?.content ?? '';
if (!content) throw new Error('Empty response');
console.log('OK');
const usage = normalizeOpenAIUsage(data.usage);
return { content, usage };
} catch (e) {
if (e.name === 'AbortError') throw new Error(`Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s`);
throw e;
} finally {
clearTimeout(timerId);
}
}
const models = await loadFreeModels();
let lastError;
if (models.length === 0) {
throw new Error(
'No free OpenRouter models are available. Model loading may have failed, or your account currently has no free models.'
);
}
// Build the active (non-blacklisted) model list in rotation order
const active = models.filter(m => !blacklistedModels.has(m));
if (active.length === 0) throw new Error('All loaded models have been blacklisted this session.');
for (let attempt = 0; attempt < active.length; attempt++) {
const model = active[(modelIndex % active.length + attempt) % active.length];
activeModel = model;
process.stdout.write(`[model] ${model} ... `);
try {
const body = JSON.stringify({
model,
messages: [
buildCachedSystemMessage(systemPrompt),
{ role: 'user', content: userMessage },View on GitHub (pinned to aac998c7ed)