{"record":{"id":"4edfd2585dd8f189","repo":"decolua/9router","slug":"invalid-model-format-4edfd2","errorCode":null,"errorMessage":"Invalid model format","messagePattern":"Invalid model format","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"src/sse/handlers/embeddings.js","lineNumber":80,"sourceCode":"    if (!valid) {\n      log.warn(\"AUTH\", \"Invalid API key (requireApiKey=true)\");\n      return errorResponse(HTTP_STATUS.UNAUTHORIZED, \"Invalid API key\");\n    }\n  }\n\n  if (!modelStr) {\n    log.warn(\"EMBEDDINGS\", \"Missing model\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing model\");\n  }\n\n  if (!body.input) {\n    log.warn(\"EMBEDDINGS\", \"Missing input\");\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Missing required field: input\");\n  }\n\n  const modelInfo = await getModelInfo(modelStr);\n  if (!modelInfo.provider) {\n    log.warn(\"EMBEDDINGS\", \"Invalid model format\", { model: modelStr });\n    return errorResponse(HTTP_STATUS.BAD_REQUEST, \"Invalid model format\");\n  }\n\n  const { provider, model } = modelInfo;\n\n  if (modelStr !== `${provider}/${model}`) {\n    log.info(\"ROUTING\", `${modelStr} → ${provider}/${model}`);\n  } else {\n    log.info(\"ROUTING\", `Provider: ${provider}, Model: ${model}`);\n  }\n\n  // Credential + fallback loop (mirrors handleChat)\n  const excludeConnectionIds = new Set();\n  let lastError = null;\n  let lastStatus = null;\n\n  while (true) {\n    const credentials = await getProviderCredentials(provider, excludeConnectionIds, model);","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/sse/handlers/embeddings.js#L62-L98","documentation":"HTTP 400 returned when getModelInfo cannot parse a provider out of the `model` string (no `.provider` in the parsed result). The router routes by `provider/model` identifiers, so an unparseable or unknown model string cannot be mapped to any upstream provider.","triggerScenarios":"POST to embeddings with model values like \"text-embedding-3-small\" (no provider prefix), \"foo/bar\" where foo is not a registered provider, or a typo'd provider id.","commonSituations":"Using a bare OpenAI model name copied from OpenAI docs without the router's provider prefix; provider renamed or removed from the registry; alias out of date after upgrading the router.","solutions":["Prefix the model with a registered provider: `provider/model` (e.g. \"openai/text-embedding-3-small\")","Check the dashboard model list (or /v1/models) for valid model identifiers","Run the alias/registry regression scripts if you recently changed provider registry files","Use a configured alias/combo name instead of a raw model string"],"exampleFix":"// before\n{ model: 'text-embedding-3-small', input: ['hi'] }\n// after\n{ model: 'openai/text-embedding-3-small', input: ['hi'] }","handlingStrategy":"validation","validationCode":"const PROVIDERS = ['openai','gemini','voyage','jina']; // from /v1/models or dashboard\nfunction parseModelId(s) {\n  const i = s.indexOf('/');\n  return i > 0 && PROVIDERS.includes(s.slice(0, i)) ? s : null;\n}\nif (!parseModelId(model)) throw new Error(`Model must be \"provider/model\": ${model}`);","typeGuard":"function isQualifiedModelId(s) {\n  return typeof s === 'string' && /^[a-z0-9-]+\\/[\\w.\\-]+$/.test(s);\n}","tryCatchPattern":"if (res.status === 400 && (await res.text()).includes('Invalid model format')) {\n  console.error(`\"${model}\" is not a routable model id — fetch /v1/models for valid ids`);\n}","preventionTips":["Always use provider-prefixed model ids from the dashboard model list","Re-validate model ids after upgrading the router (registry/aliases can change)","Prefer aliases/combos configured in the dashboard over hand-typed ids"],"tags":["validation","bad-request","model-format"],"backgroundTag":"invalid-model-format","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}