{"record":{"id":"b4b498785a74dbdb","repo":"mastra-ai/mastra","slug":"expected-gatewayprefix-in-model-router-id-ro","errorCode":null,"errorMessage":"Expected ${gatewayPrefix}/ in model router ID ${routerId}","messagePattern":"Expected (.+?)/ in model router ID (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/llm/model/gateway-resolver.ts","lineNumber":10,"sourceCode":"export type ResolvedModelConfig = {\n  url: string | false;\n  headers: Record<string, string>;\n  resolvedModelId: string;\n  fullModelId: string;\n};\n\nexport function parseModelRouterId(routerId: string, gatewayPrefix?: string): { providerId: string; modelId: string } {\n  if (gatewayPrefix && !routerId.startsWith(`${gatewayPrefix}/`)) {\n    throw new Error(`Expected ${gatewayPrefix}/ in model router ID ${routerId}`);\n  }\n\n  const idParts = routerId.split('/');\n\n  // Azure OpenAI uses 2-part format (azure-openai/deployment), others use 3-part (gateway/provider/model)\n  if (gatewayPrefix === 'azure-openai') {\n    const modelId = idParts.slice(1).join('/');\n    if (!modelId) {\n      throw new Error(`Expected format azure-openai/deployment-name, but got ${routerId}`);\n    }\n    return {\n      providerId: 'azure-openai',\n      modelId, // Deployment name\n    };\n  }\n\n  // Provider-equals-gateway: a gateway whose provider id is the same as its\n  // gateway id (e.g. amazon-bedrock) uses a 2-part router id (gateway/model),","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/llm/model/gateway-resolver.ts#L1-L28","documentation":"parseModelRouterId parses a router id like \"gateway/provider/model\". When an optional gatewayPrefix argument is given, the id must start with that prefix followed by '/'. If it doesn't, the function throws immediately rather than mis-parsing segments. This guards against resolving a model under the wrong gateway.","triggerScenarios":"parseModelRouterId(\"openai/gpt-4o\", \"openai-compatible\") (wrong prefix), or passing a bare model id like \"gpt-4o\" (no prefix at all) when a gatewayPrefix is expected.","commonSituations":"Mixing ids from different gateways (storing an azure id but resolving with an openai-compatible prefix); ids stored before a gateway migration; stripping the prefix accidentally when reading from DB or env.","solutions":["Pass an id that starts with the expected prefix, e.g. `${gatewayPrefix}/provider/model`.","If the prefix is uncertain, call parseModelRouterId without gatewayPrefix and validate the first segment yourself.","Verify the stored model id's gateway matches the resolver's expected gatewayPrefix before parsing."],"exampleFix":"// before\nparseModelRouterId(\"openai/gpt-4o\", \"openai-compatible\");\n// after\nparseModelRouterId(\"openai-compatible/openai/gpt-4o\", \"openai-compatible\");","handlingStrategy":"validation","validationCode":"function assertRouterId(routerId, gatewayPrefix) {\n  if (gatewayPrefix && !routerId.startsWith(`${gatewayPrefix}/`)) {\n    throw new Error(`Router id must start with \"${gatewayPrefix}/\", got: ${routerId}`);\n  }\n}\nassertRouterId(routerId, 'openai-compatible');\nparseModelRouterId(routerId, 'openai-compatible');","typeGuard":"const hasGatewayPrefix = (id, prefix) => id.startsWith(`${prefix}/`);","tryCatchPattern":"try {\n  const { providerId, modelId } = parseModelRouterId(routerId, gatewayPrefix);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Expected ')) {\n    console.error(`Router id \"${routerId}\" does not match gateway \"${gatewayPrefix}\"`);\n  } else throw e;\n}","preventionTips":["Store the gateway prefix alongside the id so the expected prefix is never guessed.","Validate prefix match before parsing when ids come from multiple gateways.","Add tests covering ids from every gateway your app uses."],"tags":["parsing","validation","gateway"],"backgroundTag":"invalid-model-string-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}