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

  1. Use the fully qualified OpenRouter model id 'author/slug' (e.g. 'anthropic/claude-3.5-sonnet')
  2. Validate/normalize user input against this pattern before calling the API: /^[^/]+\/.+$/
  3. 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

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


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/eb3b83648cead108. Report an issue: GitHub.