mastra-ai/mastra · error

MastraCode requires at least one mode

Error message

MastraCode requires at least one mode

What it means

createMastraCodeAgentController() derives defaultModeId by finding a mode with metadata.default === true, then the 'build' mode, then modes[0]. If the modes array is empty, all lookups yield undefined and the controller throws, since MastraCode cannot operate without at least one agent mode.

Source

Thrown at mastracode/sdk/src/index.ts:1059

    ? undefined
    : selectPreferredOMPack(startupAccess, activeProviderId)?.modelId;
  const effectiveObserverModel = resolveOmRoleModel(globalSettings, 'observer', builtinOmPacks) || preferredOmModel;
  const effectiveReflectorModel = resolveOmRoleModel(globalSettings, 'reflector', builtinOmPacks) || preferredOmModel;
  const effectiveObservationThreshold = globalSettings.models.omObservationThreshold ?? undefined;
  const effectiveReflectionThreshold = globalSettings.models.omReflectionThreshold ?? undefined;
  const effectiveCavemanObservations = globalSettings.models.omCavemanObservations ?? undefined;
  const effectiveObserveAttachments = globalSettings.models.omObserveAttachments ?? 'auto';

  const modes = addPluginToolsToModeAllowlists(
    applyEffectiveDefaultsToModes(config?.modes ? config.modes : defaultModes, effectiveDefaults),
    Object.keys(pluginTools),
  );
  const defaultModeId =
    modes.find(mode => mode.metadata?.default === true)?.id ??
    modes.find(mode => mode.id === 'build')?.id ??
    modes[0]?.id;
  if (!defaultModeId) {
    throw new Error('MastraCode requires at least one mode');
  }

  // Map subagent types to mode models: explore→fast, plan→plan, execute→build
  // const subagentModeMap: Record<string, string> = { explore: 'fast', plan: 'plan', execute: 'build' };
  // Subagents inherit workspace tools from the parent agent's workspace automatically.
  // Apply disabledTools filter to both default and custom subagents.
  // const subagents = [];

  // Build initial state with global preferences. OM knobs are skipped when the
  // host persists memory settings elsewhere (`disableSettingsOmSeed`) so the
  // machine-local settings.json never leaks into server sessions.
  const globalInitialState: Partial<MastraCodeState> = {};
  if (!config?.disableSettingsOmSeed) {
    if (effectiveObserverModel) {
      globalInitialState.observerModelId = effectiveObserverModel;
    }
    if (effectiveReflectorModel) {
      globalInitialState.reflectorModelId = effectiveReflectorModel;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Ensure at least one mode is registered, ideally one with metadata: { default: true } or id: 'build'.
  2. Log the resolved modes array before creating the controller to find what filtered them all out.
  3. If building modes dynamically, fall back to including the built-in 'build' mode when the list is empty.

Example fix

// before
const modes = enabledModes.filter(m => flags[m.id]); // can be []
// after
const resolved = enabledModes.filter(m => flags[m.id]);
const modes = resolved.length > 0 ? resolved : [buildMode];
Defensive patterns

Strategy: validation

Validate before calling

const hasDefault = modes.some(m => m.metadata?.default === true || m.id === 'build');
if (!Array.isArray(modes) || modes.length === 0) {
  throw new Error('modes config resolved to an empty list; register at least one mode');
}

Type guard

function hasAtLeastOneMode(ms: unknown): ms is NonEmptyArray<MastraCodeMode> {
  return Array.isArray(ms) && ms.length > 0;
}

Try / catch

try {
  controller = await createMastraCodeAgentController({ ...cfg, modes });
} catch (err) {
  if (err instanceof Error && err.message.includes('requires at least one mode')) {
    controller = await createMastraCodeAgentController({ ...cfg, modes: [defaultBuildMode] });
  } else { throw err; }
}

Prevention

When it happens

Trigger: Constructing the controller with a modes configuration that resolves to an empty array — e.g. all modes filtered out by feature flags/disabledTools config, a custom modes loader returning [], or misconfigured modes config paths yielding no entries.

Common situations: Custom setups that override modes but forget to include a default/'build' mode; config-driven mode gating that excludes everything in a minimal environment; typos in mode registration that silently skip all modes.

Related errors


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