musistudio/claude-code-router · error
OpenRouter model id must be author/slug: ${model}
Error message
OpenRouter model id must be author/slug: ${model} What it means
OpenRouter model ids must have the form author/slug (exactly one slash separating vendor and model). loadModelEndpoints splits on "/" and requires both parts; anything else (no slash, leading slash, empty slug) is rejected before any HTTP call.
Source
Thrown at packages/core/src/plugins/built-ins/openrouter-discount-provider-router.ts:323
}
const failure = endpointFailureCache.get(cacheKey);
if (failure) {
const cooldownAge = now - failure.failedAt;
if (cooldownAge < endpointFailureCooldownMs) {
const remainingMs = endpointFailureCooldownMs - cooldownAge;
throw new Error(`OpenRouter endpoints request is cooling down after failure (${Math.ceil(remainingMs / 1000)}s left): ${failure.message}`);
}
endpointFailureCache.delete(cacheKey);
}
const inflight = endpointInflight.get(cacheKey);
if (inflight) {
return inflight;
}
const [author, ...slugParts] = model.split("/");
const slug = slugParts.join("/");
if (!author || !slug) {
throw new Error(`OpenRouter model id must be author/slug: ${model}`);
}
const url = `${trimRight(apiRoot, "/")}/api/v1/models/${encodeURIComponent(author)}/${encodeURIComponent(slug)}/endpoints`;
const promise = fetchModelEndpoints(url).then((endpoints) => {
endpointFailureCache.delete(cacheKey);
if (endpoints.length > 0) {
endpointCache.set(cacheKey, { endpoints, fetchedAt: Date.now() });
pruneEndpointCache();
}
return endpoints;
}).catch((error) => {
endpointFailureCache.set(cacheKey, {
failedAt: Date.now(),
message: formatError(error)
});
pruneEndpointFailureCache();
throw error;
}).finally(() => {View on GitHub (pinned to 99f24806c6)
Solutions
- Use the full author/slug id from OpenRouter's model listing
- Validate the id format before calling
- Trim whitespace/slashes from config-provided model ids
Example fix
// before
loadModelEndpoints("gpt-4o");
// after
loadModelEndpoints("openai/gpt-4o"); Defensive patterns
Strategy: validation
Validate before calling
const parts = model.split("/");
if (parts.length < 2 || !parts[0] || parts.slice(1).join("/") === "") throw new TypeError(`bad model id: ${model}`); Type guard
const isOpenRouterModelId = (m: string): boolean => { const [a, ...rest] = m.split("/"); return Boolean(a && rest.join("/")); }; Try / catch
try { await loadModelEndpoints(model); } catch (e) { if (e instanceof Error && e.message.includes("must be author/slug")) return badRequest(); throw e; } Prevention
- Pull ids verbatim from OpenRouter's models endpoint
- Validate ids at config load time
When it happens
Trigger: Passing a bare model name like "gpt-4o", "openai/" (empty slug), or a multi-segment path where the slug ends up empty.
Common situations: Users pasting OpenAI-style model names, or ids constructed by joining config values where one part is empty.
Related errors
- OpenRouter model id must be author/slug: ${model}
- The CCR artifact endpoint returned a non-media content type.
- The CCR media artifact exceeds the inline preview size limit
- Compressed CCR media artifacts are not accepted for inline p
- The CCR artifact response was empty.
AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27).
Data as JSON: /api/errors/bed5a03bdb797da5.
Report an issue: GitHub.