musistudio/claude-code-router · error · Error
OpenRouter model id must be author/slug: ${model}
Error message
OpenRouter model id must be author/slug: ${model} What it means
modelEndpointsPath expects OpenRouter model ids in 'author/slug' form and throws when the string has no slash or nothing after it. The path template /api/v1/models/{author}/{slug}/endpoints cannot be built safely from a malformed id.
Source
Thrown at packages/core/src/providers/openrouter-provider-catalog.ts:222
date.setUTCDate(date.getUTCDate() - 1);
return date.toISOString().slice(0, 10);
}
function bestPercent(left: number | undefined, right: number | undefined): number | undefined {
if (left === undefined) {
return right;
}
if (right === undefined) {
return left;
}
return Math.max(left, right);
}
function modelEndpointsPath(model: string): string {
const [author, ...slugParts] = model.split("/");
const slug = slugParts.join("/");
if (!author || !slug) {
throw new Error(`OpenRouter model id must be author/slug: ${model}`);
}
return `/api/v1/models/${encodeURIComponent(author)}/${encodeURIComponent(slug)}/endpoints`;
}
function baseProviderSlug(value: string): string {
return slugify(value.split("/")[0] ?? "");
}
function providerNameFromSlug(slug: string): string {
return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
}
function uniqueDisplayStrings(values: unknown[]): string[] {
const byKey = new Map<string, string>();
for (const value of values) {
const text = stringValue(value);
const key = text.toLowerCase();
if (!text || byKey.has(key)) {View on GitHub (pinned to 99f24806c6)
Solutions
- Use the fully qualified OpenRouter model id 'author/slug' (e.g. 'anthropic/claude-3.5-sonnet')
- Validate/normalize user input against this pattern before calling the API: /^[^/]+\/.+$/
- Look up the correct slug in the OpenRouter models listing if unsure of the author prefix
Example fix
// before
getOpenRouterProviderCatalog({ model: "claude-3.5-sonnet" })
// after
getOpenRouterProviderCatalog({ model: "anthropic/claude-3.5-sonnet" }) Defensive patterns
Strategy: validation
Validate before calling
const MODEL_ID = /^[^/\s]+\/[^/\s]+$/;
if (!MODEL_ID.test(model)) throw new Error(`model must be author/slug: ${model}`); Type guard
function isOpenRouterModelId(v: string): boolean { return /^[^/\s]+\/[^/\s]+$/.test(v.trim()); } Prevention
- Store fully qualified model ids in config
- Validate free-text model fields before calling the catalog
When it happens
Trigger: Calling getOpenRouterProviderCatalog with model set to something without a '/', such as 'gpt-4o', or with an empty slug like 'openai/'.
Common situations: Passing a plain model name instead of the OpenRouter qualified id (e.g. 'claude-3' vs 'anthropic/claude-3'); user-typed model ids from a config field; trailing-slash or whitespace-mangled ids.
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/eb3b83648cead108.
Report an issue: GitHub.