decolua/9router · error
Invalid API key
Error message
Invalid API key
What it means
Second stage of the API-key guard in handleSearch (src/sse/handlers/search.js:56): a key was present on the request, but await isValidApiKey(apiKey) returned false, so the handler returns HTTP 401 'Invalid API key'. The gateway validates the supplied key against keys issued in its dashboard/settings store; an unrecognized or revoked key is rejected even though requireApiKey enforcement is satisfied in form.
Source
Thrown at src/sse/handlers/search.js:56
// Log API key (masked)
const apiKey = extractApiKey(request);
if (apiKey) {
log.debug("AUTH", `API Key: ${log.maskKey(apiKey)}`);
} 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 (!providerInput || typeof providerInput !== "string") {
log.warn("SEARCH", "Missing provider/model");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing required field: provider (or model)");
}
if (!query || typeof query !== "string" || !query.trim()) {
log.warn("SEARCH", "Missing query");
return errorResponse(HTTP_STATUS.BAD_REQUEST, "Missing required field: query");
}
// Combo expansion: providerInput may be a combo name → run fallback/round-robin across providers
const combos = await getCombos();
const comboModels = getComboModelsFromData(providerInput, combos);
if (comboModels) {View on GitHub (pinned to 90b52e06ff)
Solutions
- Copy a currently valid key from the 9Router dashboard API-keys page and update the client config.
- Trim whitespace and ensure the header format is exactly 'Authorization: Bearer <key>' with no duplicate 'Bearer Bearer'.
- If the key store was invalidated by a secret change (JWT_SECRET/API_KEY_SECRET), issue new keys and redeploy clients.
- Confirm you are pointing at the correct 9Router instance/environment that issued the key.
- Check the server log line 'Invalid API key (requireApiKey=true)' timing against key rotations to confirm which value was rejected.
Example fix
// before: stale/rotated key or wrong env
headers: { Authorization: `Bearer ${OLD_KEY}` }
// after: reload a fresh key, trimmed
const key = (process.env.ROUTER_API_KEY || '').trim();
if (!key) throw new Error('ROUTER_API_KEY is not set');
headers: { Authorization: `Bearer ${key}` } Defensive patterns
Strategy: validation
Validate before calling
const key = (process.env.ROUTER_API_KEY || '').trim();
if (!key || key === 'changeme' || key.length < 16) throw new Error('ROUTER_API_KEY looks like a placeholder or rotated-out key');
const probe = await fetch(base + '/v1/models', { headers: { Authorization: `Bearer ${key}` } });
if (probe.status === 401) throw new Error('Key rejected by gateway — re-issue from dashboard'); Type guard
function looksLikeRouterKey(k) {
return typeof k === 'string' && k.trim().length >= 16 && !/\s/.test(k);
} Try / catch
const res = await doSearch();
if (res.status === 401 && (await res.clone().text()).includes('Invalid API key')) {
throw new Error('API key rejected: fetch a fresh key from the 9Router dashboard and update env');
} Prevention
- Rotate keys centrally: update the shared secret store, then all clients, in one deployment step.
- Strip whitespace/newlines when loading keys from .env files.
- Never reuse an upstream provider key as the gateway key — use keys issued by 9Router.
- Add a startup health-check call (/v1/models) that fails fast on 401 so bad keys surface immediately.
When it happens
Trigger: POST to the /v1 search endpoint with requireApiKey=true and an API key header present whose value fails isValidApiKey: key was regenerated/rotated in the dashboard, key belongs to a different 9Router instance, whitespace or 'Bearer ' prefix handling mismatch, or a placeholder env var value was sent.
Common situations: Keys rotated after a security incident without updating client env vars; copying a key from a staging instance to production; JWT_SECRET or API_KEY_SECRET changed so previously valid derived keys no longer validate; trailing newline/space in the .env value; using an upstream provider key instead of the 9Router-issued gateway key.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/9ca0e2bb2de0e776.
Report an issue: GitHub.