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
- Point transitionsTo at a different mode id, or omit it to use the default flow
- If looping behavior is needed, alternate between two modes or re-enter the mode via the runtime API instead of a self-transition
- 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
- Never default transitionsTo to the mode's own id in generators
- Treat mode graphs as DAGs; lint for cycles before construction
- Omit transitionsTo when the next step equals the current mode
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
- Mode "${mode.id}" transitionsTo references unknown mode "${m
- Duplicate mode id "${mode.id}" found when creating the Agent
- Mode "${modeRecord.id}" cannot set both "tools" and "additio
- Default mode not found: ${config.defaultModeId}
- agent controller "${controllerId}" not found
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/d7c9bbe37b02b05b.
Report an issue: GitHub.