santifer/career-ops · critical · Error
OPENROUTER_API_KEY not found. Copy .env.example to .env and
Error message
OPENROUTER_API_KEY not found. Copy .env.example to .env and add your API key. Free key: https://openrouter.ai
What it means
Thrown by callOpenRouter() in openrouter-runner.mjs when process.env.OPENROUTER_API_KEY is falsy at call time. This is the hard precondition before any HTTP request — there is no fallback, no anonymous access. The message directs the user to copy .env.example to .env and obtain a free key from openrouter.ai.
Source
Thrown at openrouter-runner.mjs:201
// Providers that support caching (Anthropic, Gemini, …) reuse the prefix across
// back-to-back calls within the cache TTL; providers that don't simply ignore
// the field, so this is a safe passthrough that never changes the prompt text.
export function buildCachedSystemMessage(systemPrompt) {
return {
role: 'system',
content: [
{ type: 'text', text: systemPrompt, cache_control: { type: 'ephemeral' } },
],
};
}
// ---------------------------------------------------------------------------
// OpenRouter API call — automatic model rotation with fallback
// ---------------------------------------------------------------------------
async function callOpenRouter(systemPrompt, userMessage) {
const key = process.env.OPENROUTER_API_KEY;
if (!key) {
throw new Error(
'OPENROUTER_API_KEY not found.\n' +
'Copy .env.example to .env and add your API key.\n' +
'Free key: https://openrouter.ai'
);
}
const pinnedModel = process.env.CAREER_OPS_MODEL;
if (pinnedModel) {
activeModel = pinnedModel;
process.stdout.write(`[model] ${pinnedModel} (pinned) ... `);
const body = JSON.stringify({
model: pinnedModel,
messages: [
buildCachedSystemMessage(systemPrompt),
{ role: 'user', content: userMessage },
],
max_tokens: MAX_TOKENS,
});View on GitHub (pinned to 9b17a8ac97)
Solutions
- Copy .env.example to .env and add your key: OPENROUTER_API_KEY=sk-or-v1-...
- Get a free key at https://openrouter.ai if you don't have one.
- Verify it's visible: `node -e "console.log(Boolean(process.env.OPENROUTER_API_KEY))"` after loading .env, or export it directly in the shell.
- In CI, set the OPENROUTER_API_KEY secret on the job.
Example fix
# before: .env missing or empty # after: .env OPENROUTER_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Defensive patterns
Strategy: validation
Validate before calling
if (!process.env.OPENROUTER_API_KEY) {
throw new Error('Missing OPENROUTER_API_KEY. Copy .env.example to .env and add your key from https://openrouter.ai');
} Prevention
- Always ship a .env (gitignored) with OPENROUTER_API_KEY; never rely on it being inherited.
- In CI, set OPENROUTER_API_KEY as a repository secret on every job that calls the runner.
- Add a startup assertion so a missing key fails fast with a clear message.
- Rotate keys via the OpenRouter dashboard; don't hardcode.
When it happens
Trigger: Running the runner without OPENROUTER_API_KEY in the environment: .env missing, .env not loaded by the runner, key line commented out, or running in a shell/CI that didn't export it.
Common situations: Fresh clone without .env; CI secret not configured; renamed key; running via a wrapper that strips env; key set in a different shell than the one invoking the runner.
Related errors
- [models] Failed to fetch free model list: ${reason}. Check t
- HTTP ${resp.status}: ${t.slice(0, 120)}
- No free OpenRouter models are available. Model loading may h
- ${data.error.message}
- Empty response
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/d7c185f43e49fa73.
Report an issue: GitHub.