mastra-ai/mastra · error

Mode "${mode.id}" transitionsTo cannot reference itself

Error message

Mode "${mode.id}" transitionsTo cannot reference itself

What it means

A mode's transitionsTo defines which mode the controller switches to after that mode completes. Since self-transitions would create an infinite loop with no progress, validateModes rejects any mode whose transitionsTo equals its own id at construction time.

Source

Thrown at packages/core/src/agent-controller/agent-controller.ts:81

  for (const mode of modes) {
    if (modeIds.has(mode.id)) {
      throw new Error(`Duplicate mode id "${mode.id}" found when creating the AgentController`);
    }

    modeIds.add(mode.id);

    const modeRecord = mode as unknown as { id: string; tools?: unknown; additionalTools?: unknown };
    if (modeRecord.tools && modeRecord.additionalTools) {
      throw new Error(
        `Mode "${modeRecord.id}" cannot set both "tools" and "additionalTools" - choose replace OR augment`,
      );
    }
  }

  for (const mode of modes) {
    if (mode.transitionsTo === mode.id) {
      throw new Error(`Mode "${mode.id}" transitionsTo cannot reference itself`);
    }
    if (mode.transitionsTo && !modeIds.has(mode.transitionsTo)) {
      throw new Error(`Mode "${mode.id}" transitionsTo references unknown mode "${mode.transitionsTo}"`);
    }
  }
}

/**
 * Build a user-facing message for a non-success stream finish reason.
 *
 * Anthropic's classifier blocks / model refusals (e.g. `claude-fable-5`) surface
 * through the AI SDK as a `content-filter` finish reason, with details on
 * `providerMetadata.anthropic.stopDetails`. Without explicit handling these
 * runs end on an empty assistant message with no error, so the run appears to
 * silently stop. Returning a message here lets the controller finalize the run
 * into an explicit terminal error state.
 */
/**

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Point transitionsTo at a different mode id, or omit it to use the default flow
  2. If looping behavior is needed, alternate between two modes or re-enter the mode via the runtime API instead of a self-transition
  3. Validate generated configs before passing them to the constructor

Example fix

// before
{ id: 'plan', transitionsTo: 'plan' }
// after
{ id: 'plan', transitionsTo: 'execute' }
Defensive patterns

Strategy: validation

Validate before calling

function assertNoSelfTransitions(modes: { id: string; transitionsTo?: string }[]): void {
  for (const m of modes) {
    if (m.transitionsTo === m.id) throw new Error(`Mode "${m.id}" cannot transition to itself`);
  }
}
assertNoSelfTransitions(config.modes);

Try / catch

try {
  new AgentController(config);
} catch (e) {
  if ((e as Error).message.includes('transitionsTo cannot reference itself')) {
    // fix or remove the self-referencing transitionsTo, then reconstruct
  }
}

Prevention

When it happens

Trigger: Creating an AgentController where a mode is declared as e.g. { id: 'plan', transitionsTo: 'plan', ... } — usually from templated/generated configs that default transitionsTo to the current mode id.

Common situations: Auto-generating mode configs from a map where each mode's transitionsTo defaulted to itself; misunderstanding transitionsTo as 'stay in this mode when done'; refactoring renames that collided ids.

Related errors


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