mastra-ai/mastra · error · Error

${PLUGIN_MANIFEST_FILE} contains multiple plugins. Provide a

Error message

${PLUGIN_MANIFEST_FILE} contains multiple plugins. Provide an entry path for one of: ${manifest.plugins.map(plugin => `${plugin.id} (${plugin.entry})`).join(', ')}

What it means

getSingleManifestPlugin() resolves the one plugin described by `.mastracode-plugin.json` when no explicit entry path is given. If the manifest declares more than one plugin, no single default can be chosen, so it throws and lists each plugin's id and entry so you can specify one.

Source

Thrown at mastracode/sdk/src/plugins/manifest.ts:59

export function upsertPluginManifestEntry(rootDir: string, entry: PluginManifestEntry): void {
  const manifest = loadPluginManifest(rootDir) ?? { plugins: [] };
  const validatedEntry = validateManifestEntry(entry, manifest.plugins.length);
  const existingIndex = manifest.plugins.findIndex(plugin => plugin.id === validatedEntry.id);
  if (existingIndex >= 0) {
    manifest.plugins[existingIndex] = validateManifestEntry(validatedEntry, existingIndex);
  } else {
    manifest.plugins.push(validatedEntry);
  }
  savePluginManifest(rootDir, manifest);
}

export function getSingleManifestPlugin(rootDir: string): PluginManifestEntry | undefined {
  const manifest = loadPluginManifest(rootDir);
  if (!manifest) return undefined;
  if (manifest.plugins.length === 0) return undefined;
  if (manifest.plugins.length > 1) {
    throw new Error(
      `${PLUGIN_MANIFEST_FILE} contains multiple plugins. Provide an entry path for one of: ${manifest.plugins
        .map(plugin => `${plugin.id} (${plugin.entry})`)
        .join(', ')}`,
    );
  }
  return manifest.plugins[0];
}

function validateManifestEntry(entry: unknown, index: number): PluginManifestEntry {
  if (!entry || typeof entry !== 'object') {
    throw new Error(`${PLUGIN_MANIFEST_FILE} plugin at index ${index} must be an object`);
  }
  const candidate = entry as { id?: unknown; name?: unknown; entry?: unknown };
  if (typeof candidate.id !== 'string' || candidate.id.length === 0) {
    throw new Error(`${PLUGIN_MANIFEST_FILE} plugin at index ${index} must include an id`);
  }
  if (typeof candidate.entry !== 'string' || candidate.entry.length === 0) {
    throw new Error(`${PLUGIN_MANIFEST_FILE} plugin ${candidate.id} must include an entry`);

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass an explicit entry path / select the desired plugin at the manifestPlugin call site instead of relying on single-plugin inference.
  2. Reduce the manifest to one plugin if only one is intended; remove stale entries.
  3. Split plugins into separate directories, each with its own `.mastracode-plugin.json` containing one entry.
  4. Pick from the ids listed in the error message — it enumerates `id (entry)` for every plugin.

Example fix

// before
manifestPlugin(rootDir)
// after — point at one entry explicitly
manifestPlugin(rootDir, { entry: './plugins/beta.ts' })
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
const m = JSON.parse(fs.readFileSync(path.join(rootDir, '.mastracode-plugin.json'), 'utf8'));
if (Array.isArray(m.plugins) && m.plugins.length > 1) {
  // pass an explicit entry to manifestPlugin instead of relying on the single-plugin path
  console.warn('Multiple plugins:', m.plugins.map((p: any) => p.id));
}

Type guard

function hasSinglePlugin(manifest: { plugins: unknown[] }): manifest is { plugins: [unknown] } {
  return manifest.plugins.length === 1;
}

Try / catch

try {
  const plugin = getSingleManifestPlugin(rootDir);
} catch (error) {
  if (error instanceof Error && error.message.includes('contains multiple plugins')) {
    console.error('Specify an entry path; candidates:', error.message);
    return;
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling getSingleManifestPlugin (via manifestPlugin) against a rootDir whose manifest contains 2+ entries, without passing an entry path/selector for one of them.

Common situations: Adding a second plugin to a shared manifest and forgetting that the manifest-plugin loader expects exactly one; multi-plugin monorepo where the build tooling used to auto-pick the single entry; leftover entries from copied manifests.

Related errors


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