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
- Pass an explicit entry path / select the desired plugin at the manifestPlugin call site instead of relying on single-plugin inference.
- Reduce the manifest to one plugin if only one is intended; remove stale entries.
- Split plugins into separate directories, each with its own `.mastracode-plugin.json` containing one entry.
- 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
- Only rely on getSingleManifestPlugin when the manifest is guaranteed to hold one entry.
- Pass an explicit entry path to manifestPlugin for multi-plugin manifests.
- Clean stale plugin entries out of shared manifests.
- Keep one manifest per plugin directory if you use single-plugin inference.
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
- Could not parse ${PLUGIN_MANIFEST_FILE}: ${error instanceof
- ${PLUGIN_MANIFEST_FILE} must contain a plugins array
- ${PLUGIN_MANIFEST_FILE} plugin at index ${index} must be an
- Okta domain is required. Provide it in the options or set OK
- Okta API token is required for RBAC. Provide it in the optio
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/2ec8e7dba1575c81.
Report an issue: GitHub.