{"record":{"id":"5456d50f7e0b7b95","repo":"mastra-ai/mastra","slug":"invalid-model-string-format-config-expected","errorCode":null,"errorMessage":"Invalid model string format: \"${config}\". Expected format: \"mastra/provider/model\"","messagePattern":"Invalid model string format: \"(.+?)\"\\. Expected format: \"mastra/provider/model\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/llm/model/embedding-router.ts","lineNumber":140,"sourceCode":"  // spec). This wrapper keeps exposing the V2 interface; doEmbed adapts.\n  private providerModel: EmbeddingModelV3;\n\n  constructor(config: string | OpenAICompatibleConfig) {\n    // Normalize config to always have provider and model IDs\n    let normalizedConfig: {\n      providerId: string;\n      modelId: string;\n      url?: string;\n      apiKey?: string;\n      headers?: Record<string, string>;\n    };\n\n    if (typeof config === 'string') {\n      // Parse provider/model or gateway/provider/model from string.\n      const parts = config.split('/');\n      if (parts[0] === MASTRA_GATEWAY_ID) {\n        if (parts.length < 3) {\n          throw new Error(`Invalid model string format: \"${config}\". Expected format: \"mastra/provider/model\"`);\n        }\n        normalizedConfig = { providerId: MASTRA_GATEWAY_ID, modelId: parts.slice(1).join('/') };\n      } else {\n        if (parts.length !== 2) {\n          throw new Error(`Invalid model string format: \"${config}\". Expected format: \"provider/model\"`);\n        }\n        const [providerId, modelId] = parts as [string, string];\n        normalizedConfig = { providerId, modelId };\n      }\n    } else if ('providerId' in config && 'modelId' in config) {\n      normalizedConfig = {\n        providerId: config.providerId,\n        modelId: config.modelId,\n        url: config.url,\n        apiKey: config.apiKey,\n        headers: config.headers,\n      };\n    } else {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/llm/model/embedding-router.ts#L122-L158","documentation":"The embedding model router parses model strings into provider/model (or gateway/provider/model) identifiers. When the string starts with the Mastra gateway id ('mastra'), it requires at least 3 slash-separated segments; otherwise it throws 'Invalid model string format: \"<config>\". Expected format: \"mastra/provider/model\"'. This guard exists because gateway model IDs may themselves contain slashes, so the remainder is re-joined with parts.slice(1).","triggerScenarios":"Constructing an embedding model/router with a string like 'mastra/openai' (only 2 segments) or 'mastra' alone, i.e. any 'mastra/...' string without a provider AND model segment after the gateway id.","commonSituations":"Migrating from 'provider/model' strings to gateway format and dropping a segment; assuming 'mastra/provider' is enough; dynamically building the model string and truncating the model id; typos like 'mastra/openai/text-embedding-3/' — those parse, but 'mastra/openai' does not.","solutions":["Use the full three-segment format: 'mastra/<provider>/<model>', e.g. 'mastra/openai/text-embedding-3-small'","If not using the Mastra gateway, drop the 'mastra/' prefix and pass '<provider>/<model>' instead","Log/print the config value being passed to catch dynamic string-building bugs","Validate the model string with a regex before constructing the router"],"exampleFix":"// before\nnew EmbeddingRouter('mastra/openai'); // throws\n// after\nnew EmbeddingRouter('mastra/openai/text-embedding-3-small');","handlingStrategy":"validation","validationCode":"function assertModelString(config) {\n  const parts = config.split('/');\n  if (parts[0] === 'mastra') {\n    if (parts.length < 3) throw new Error(`Invalid model string: \"${config}\". Expected \"mastra/provider/model\"`);\n  } else if (parts.length !== 2) {\n    throw new Error(`Invalid model string: \"${config}\". Expected \"provider/model\"`);\n  }\n}\nassertModelString(modelConfig); // call before constructing the router","typeGuard":"function isValidModelString(config) {\n  if (typeof config !== 'string') return false;\n  const parts = config.split('/');\n  return parts[0] === 'mastra' ? parts.length >= 3 : parts.length === 2;\n}","tryCatchPattern":"try {\n  const router = new EmbeddingRouter(config);\n} catch (e) {\n  if (e.message.startsWith('Invalid model string format')) {\n    console.error(`Bad MODEL env/config: ${config}. Use \"provider/model\" or \"mastra/provider/model\".`);\n    process.exit(1);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Always include both provider and model segments for gateway strings: 'mastra/<provider>/<model>'","Validate model strings with a regex before constructing the router","Log the resolved model config at startup to catch truncation in dynamic builders","Keep gateway and direct provider formats separate in config files to avoid mixed forms"],"tags":["configuration","model-string","parsing","embeddings"],"backgroundTag":"invalid-model-string-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}