mastra-ai/mastra · error

Invalid model id: ${modelId}

Error message

Invalid model id: ${modelId}

What it means

resolveModel parses a model id as `provider/rest-of-path` after stripping gateway/custom-provider prefixes. If either the provider segment or the remainder (bareModelId) is empty, the id is malformed and 'Invalid model id' is thrown. This includes ids with no slash at all (e.g. 'gpt-4o').

Source

Thrown at mastracode/sdk/src/agents/model.ts:117

    ? modelId.slice(MASTRACODE_GATEWAY_ID.length + 1)
    : modelId;
  // Deployed web registers a custom providers source (DB-backed, tenant
  // scoped); when registered it is authoritative and settings.json custom
  // providers are ignored. Undefined = local settings-based behavior.
  const customProviders = resolveCustomProviders(options?.requestContext) ?? settings.customProviders;
  // Ids selected from the shared /models catalog were previously persisted in
  // the gateway-qualified `mastracode/<customProviderId>/<model>` form, which
  // parses the provider as `mastracode` and breaks provider config lookup.
  // Normalize at resolution time (in addition to stripping at selection time)
  // so already-saved ids and any surface that persists the raw catalog id
  // still resolve to the custom provider.
  const normalizedInput = stripMastraCodeCustomProviderPrefix(bedrockNormalizedInput, customProviders);
  const isMastraGatewayModel = normalizedInput.startsWith(MASTRA_GATEWAY_PREFIX);
  const normalizedModelId = stripMastraGatewayPrefix(normalizedInput);
  const [providerId, ...modelParts] = normalizedModelId.split('/');
  const bareModelId = modelParts.join('/');
  if (!providerId || !bareModelId) {
    throw new Error(`Invalid model id: ${modelId}`);
  }

  if (providerId === AMAZON_BEDROCK_GATEWAY_ID) {
    const bedrockGateway = createAmazonBedrockGateway();
    const routerId = `${AMAZON_BEDROCK_GATEWAY_ID}/${bareModelId}`;
    const auth = bedrockGateway.resolveAuth({
      gatewayId: AMAZON_BEDROCK_GATEWAY_ID,
      providerId: AMAZON_BEDROCK_GATEWAY_ID,
      modelId: bareModelId,
      routerId,
    });
    return bedrockGateway.resolveLanguageModel({
      providerId: AMAZON_BEDROCK_GATEWAY_ID,
      modelId: bareModelId,
      apiKey: auth?.apiKey ?? '',
      headers,
    });
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Provide the full `provider/model-id` form, e.g. 'openai/gpt-4o' or 'anthropic/claude-sonnet-4'
  2. Check the model config/env for an empty or whitespace-only value and set a valid id
  3. If using gateway prefixes, keep the provider segment after the prefix (e.g. 'mastra/gateway/openai/gpt-4o' style per convention)
  4. Log the raw modelId before resolveModel to see what is actually being passed

Example fix

// before
model: 'gpt-4o'
// after
model: 'openai/gpt-4o'
Defensive patterns

Strategy: validation

Validate before calling

const VALID = /^[a-z0-9-]+\/.+$/i;
if (!VALID.test(modelId)) throw new Error(`model id must be provider/model, got: ${modelId}`);

Type guard

const isProviderQualified = (id: string): boolean =>
  id.includes('/') && id.split('/').every(p => p.length > 0);

Try / catch

try {
  const model = resolveModel(modelId);
} catch (err) {
  if ((err as Error).message.startsWith('Invalid model id')) {
    console.error(`Use provider/model form, e.g. openai/gpt-4o (got "${modelId}")`);
  } else throw err;
}

Prevention

When it happens

Trigger: resolveModel (via getObserverModel/getReflectorModel/getDynamicModel/etc.) receives a modelId that after normalization has no `provider/model` structure: empty string, bare model name without a slash, 'provider/' with empty remainder, or just '/'-degenerate strings.

Common situations: Writing 'gpt-4o' instead of 'openai/gpt-4o', config files with a blank or whitespace model field, accidentally deleting the provider prefix during edits, or copying an id with a trailing slash.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/1f6d9088ce6dd3c2. Report an issue: GitHub.