mastra-ai/mastra · error · Error
${PLUGIN_MANIFEST_FILE} plugin at index ${index} must includ
Error message
${PLUGIN_MANIFEST_FILE} plugin at index ${index} must include an id What it means
This error is thrown by validateManifestEntry when loading or updating the plugin manifest (plugins.json). Every entry in the manifest must have a non-empty string `id` so the plugin can be uniquely identified; entries without one are rejected. It exists to fail fast on malformed manifests rather than producing plugins with undefined identity downstream.
Source
Thrown at mastracode/sdk/src/plugins/manifest.ts:74
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`);
}
if (candidate.name !== undefined && typeof candidate.name !== 'string') {
throw new Error(`${PLUGIN_MANIFEST_FILE} plugin ${candidate.id} name must be a string`);
}
return {
id: candidate.id,
entry: candidate.entry,
...(candidate.name ? { name: candidate.name } : {}),
};
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Open the plugin manifest file and add a unique, non-empty string `id` to the entry at the reported index.
- Verify the key is exactly lowercase `id` (not `Id` or `name`) and its value is a string with at least one character.
- If the manifest is generated programmatically, fix the generator so every entry includes an id before writing.
- Re-run the command to confirm the manifest validates.
Example fix
// before (plugins.json entry)
{ "entry": "./dist/index.js", "name": "my-plugin" }
// after
{ "id": "my-plugin", "entry": "./dist/index.js", "name": "my-plugin" } Defensive patterns
Strategy: validation
Validate before calling
const manifest = JSON.parse(fs.readFileSync('plugins.json', 'utf8'));
manifest.forEach((entry, i) => {
if (typeof entry?.id !== 'string' || entry.id.length === 0) {
throw new Error(`plugin at index ${i} must include a non-empty string id before load`);
}
}); Type guard
function hasPluginId(entry: unknown): entry is { id: string } {
return typeof entry === 'object' && entry !== null &&
typeof (entry as { id?: unknown }).id === 'string' &&
(entry as { id: string }).id.length > 0;
} Try / catch
try {
loadPluginManifest(dir);
} catch (err) {
if (err instanceof Error && err.message.includes('must include an id')) {
console.error(`Malformed manifest: ${err.message}`);
process.exitCode = 1;
} else throw err;
} Prevention
- Keep a JSON schema or zod schema for the manifest and validate it in CI.
- Never hand-edit plugins.json without re-running the validation step.
- Use consistent lowercase key names (id, entry, name) in generators/templates.
When it happens
Trigger: Calling loadPluginManifest on a manifest file whose array contains an entry that is an object but has no `id` property, an `id` that is null/number/boolean, or `id: ""`. Also triggered by upsertPluginManifestEntry or validatedEntry passing such an object.
Common situations: Hand-editing plugins.json and forgetting the id field; renaming a plugin by deleting the id while restructuring; programmatically generating manifest entries from directory names that resolve to empty strings; JSON with a typo like "Id" instead of "id".
Related errors
- ${PLUGIN_MANIFEST_FILE} plugin ${candidate.id} must include
- Google RBAC roleMapping is required.
- Cookie password must be at least 32 characters. Set OKTA_COO
- heartbeatMs must be a finite number no greater than ${MAX_TI
- Expected a PEM-encoded public key or certificate string for
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/71d7396bcfc7caf0.
Report an issue: GitHub.