santifer/career-ops · error · Error
Empty response
Error message
Empty response
What it means
Thrown in the pinned-model branch of callOpenRouter() when the HTTP call succeeded with no error object, but data.choices[0].message.content is empty (null, undefined, or empty string). This means the model returned a well-formed but contentless response — OpenRouter accepted the request and the model produced no output tokens.
Source
Thrown at openrouter-runner.mjs:241
const resp = await fetch(OPENROUTER_API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${key}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://github.com/santifer/career-ops',
'X-Title': 'career-ops',
},
body,
signal: ctrl.signal,
});
if (!resp.ok) {
const t = await resp.text();
throw new Error(`HTTP ${resp.status}: ${t.slice(0, 120)}`);
}
const data = await resp.json();
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.'
);View on GitHub (pinned to 9b17a8ac97)
Solutions
- Retry once — empty responses are often transient single-call glitches.
- Switch pinned model (CAREER_OPS_MODEL) to a different provider/model, or unset to use rotation which skips the failing model.
- Inspect the full response (log data.choices[0]) to check finish_reason and any filter flags.
- Simplify/shorten the prompt to rule out an input-shape problem.
Defensive patterns
Strategy: retry
Try / catch
try {
return await callOpenRouter(systemPrompt, userMessage);
} catch (e) {
if (e.message === 'Empty response' && process.env.CAREER_OPS_MODEL) {
// empty content from pinned model — retry on rotation
delete process.env.CAREER_OPS_MODEL;
return await callOpenRouter(systemPrompt, userMessage);
}
throw e;
} Prevention
- Retry once on empty responses — they're often transient.
- Use rotation rather than a single pinned model so an empty response doesn't block the run.
- Inspect finish_reason in the raw response to detect content-filter empties.
- Simplify the prompt if empties recur on the same model.
When it happens
Trigger: CAREER_OPS_MODEL is set, response is 2xx with no error, but the choices[0].message.content path resolves to '' or null. Common with content-filtered responses that return finish_reason without text, or a provider hiccup yielding zero output.
Common situations: Content filtered at the provider level (finish_reason: 'content_filter') returning empty text; model glitch; very rare provider routing issue; the prompt was malformed in a way that produced no generation.
Related errors
- ${data.error.message}
- HTTP ${resp.status}: ${t.slice(0, 120)}
- Pinned model timed out after ${MODEL_TIMEOUT_MS / 1000}s
- [models] Failed to fetch free model list: ${reason}. Check t
- OPENROUTER_API_KEY not found. Copy .env.example to .env and
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/b341c7b1d3731657.
Report an issue: GitHub.