decolua/9router · error
Invalid API key
Error message
Invalid API key
What it means
API-key enforcement is on (requireApiKey=true), a key was supplied, but isValidApiKey rejected it. The handler returns 401 'Invalid API key'. The gateway validates the presented key against keys stored in its settings/database.
Source
Thrown at src/sse/handlers/chat.js:73
const authHeader = request.headers.get("Authorization");
const apiKey = extractApiKey(request);
if (authHeader && apiKey) {
const masked = log.maskKey(apiKey);
log.debug("AUTH", `API Key: ${masked}`);
} else {
log.debug("AUTH", "No API key provided (local mode)");
}
// Enforce API key if enabled in settings
const settings = await getSettings();
if (settings.requireApiKey) {
if (!apiKey) {
log.warn("AUTH", "Missing API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Missing API key");
}
const valid = await isValidApiKey(apiKey);
if (!valid) {
log.warn("AUTH", "Invalid API key (requireApiKey=true)");
return errorResponse(HTTP_STATUS.UNAUTHORIZED, "Invalid API key");
}
}
if (!modelStr) {
log.warn("CHAT", "Missing model");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing model");
}
// Bypass naming/warmup requests before combo rotation to avoid wasting rotation slots
const userAgent = request?.headers?.get("user-agent") || "";
const bypassResponse = handleBypassRequest(body, modelStr, userAgent, !!settings.ccFilterNaming);
if (bypassResponse) return bypassResponse.response || bypassResponse;
const requiredCapabilities = detectRequiredCapabilities(body);
// Check if model is a combo (has multiple models with fallback)
const comboModels = await getComboModels(modelStr);View on GitHub (pinned to 90b52e06ff)
Solutions
- Open the 9Router dashboard and copy the exact current API key, then update the client env/config.
- Check for stray whitespace/quotes in the env var: API_KEY="abc" in .env often embeds literal quotes.
- Make sure you are using a 9Router-issued key, not an upstream provider key.
- If the key was rotated, restart the client so it reloads the new value.
Example fix
// before (.env) OPENAI_API_KEY="sk-abc123" # literal quotes end up in the value // after (.env) API_KEY=sk-abc123 # correct 9Router key, no quotes/whitespace
Defensive patterns
Strategy: validation
Validate before calling
const apiKey = (process.env.NINE_ROUTER_API_KEY || '').trim();
if (!apiKey) throw new Error('missing 9router api key');
if (/^["'].*["']$/.test(process.env.NINE_ROUTER_API_KEY)) throw new Error('api key env var contains literal quotes'); Type guard
function looksLikeGatewayKey(key) {
return typeof key === 'string' && key.trim().length > 0 && !/["']/.test(key);
} Try / catch
const res = await fetch(url, { headers });
if (res.status === 401 && (await res.text()).includes('Invalid API key')) {
throw new Error('API key rejected by 9Router — re-copy the key from the dashboard');
} Prevention
- Copy keys from the dashboard verbatim; trim whitespace.
- Never put quotes inside .env values.
- Rotate keys in the client config at the same time as the dashboard.
- Use a 9Router-issued key, not an upstream provider key.
When it happens
Trigger: POST to /v1/chat/completions with Authorization: Bearer <key> where <key> does not match any key registered in the 9Router dashboard while settings.requireApiKey=true.
Common situations: Typo or stale key in the client env (pointing at an upstream provider key like OPENAI_API_KEY instead of a 9Router key); key rotated/regenerated in the dashboard but old value cached in .env; extra whitespace or quotes in the env var; multiple environments sharing one config.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Missing API key
- Missing API key
- Missing API key
- qoder PAT exchange failed: ${res.status} ${text.slice(0, 200
- GitHub API error: ${error}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/f9dfcd1268a30525.
Report an issue: GitHub.