mastra-ai/mastra · error

No model selected. Use /models to select a model first.

Error message

No model selected. Use /models to select a model first.

What it means

getDynamicModel throws this when a controller session context exists but no modelId is set in it — i.e. the user has a session but never picked a model. The message directs them to the /models command.

Source

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

 */
export function getDynamicModel(
  { requestContext }: { requestContext: RequestContext },
  settingsPath?: string,
): ResolvedModel {
  const agentControllerContext = requestContext.get('controller') as AgentControllerRequestContext<any> | undefined;

  const modelId = agentControllerContext?.session?.modelId;
  if (!modelId) {
    // A missing controller context means the run was started without session
    // request context at all (e.g. a signal delivered to an idle thread) —
    // "use /models" would mislead there, the user's selection was never the
    // problem.
    if (!agentControllerContext) {
      throw new Error(
        'No model available: this run started without a controller session context, so no model selection could be resolved.',
      );
    }
    throw new Error('No model selected. Use /models to select a model first.');
  }

  const thinkingLevel = resolveRequestThinkingLevel(agentControllerContext, settingsPath);

  return resolveModel(modelId, { thinkingLevel, remapForCodexOAuth: true, requestContext });
}

/**
 * Goal judge model resolver for the agent's `goal.judge` config. Resolves the
 * configured goal judge model through mastracode's gateway so provider
 * credentials (stored in auth storage, not just env) are injected — a bare model
 * id handed to core's default model router would fail to find the API key.
 *
 * Returns `undefined` when no judge model is configured, which keeps the goal
 * step a complete no-op (the goal mechanism requires a judge to do anything).
 *
 * `settingsPath` must be the same source `createMastraCode()` reads from so the
 * judge model and the goal budget (`goalMaxTurns`) come from one config — with a

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Run /models in the session and select a model
  2. Configure a default model for new sessions so modelId is pre-populated
  3. Re-select the model if a config migration cleared it
  4. Check why modelId was cleared if a selection was previously made (settings persistence issue)

Example fix

// before (in TUI)
> run tests with the agent   // no model chosen yet
// after
> /models   → select anthropic/claude-sonnet-4
> run tests with the agent
Defensive patterns

Strategy: validation

Validate before calling

if (agentControllerContext && !agentControllerContext.modelId) {
  throw new Error('select a model with /models before running');
}

Type guard

const hasModelSelection = (ctx: AgentControllerContext | null): boolean =>
  Boolean(ctx?.modelId);

Try / catch

try {
  await runAgent(task);
} catch (err) {
  if ((err as Error).message.includes('No model selected')) {
    console.error('Run /models to pick a model first');
  } else throw err;
}

Prevention

When it happens

Trigger: codeAgent → getDynamicModel sees a truthy agentControllerContext whose modelId is empty/undefined — an interactive session that has not made a model selection via /models.

Common situations: Fresh sessions where the user immediately invokes an action requiring the model, sessions where a previously selected model was cleared, or environments without a default model configured.

Related errors


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