mastra-ai/mastra · error · Error
Unsupported plugin entry extension "${path.extname(entryPath
Error message
Unsupported plugin entry extension "${path.extname(entryPath)}". V1 plugins must use .ts entries. What it means
importPluginModule only accepts .ts entry files in V1: the SDK relies on its TS-aware import pipeline (with mtime/size cache-busting) which is wired for TypeScript entries. Any other extension (.js, .mjs, .cjs, .json) is rejected before import.
Source
Thrown at mastracode/sdk/src/plugins/loader.ts:155
}
export function isInsideDirectory(targetPath: string, root: string): boolean {
const resolvedTarget = path.resolve(targetPath);
const resolvedRoot = path.resolve(root);
return resolvedTarget === resolvedRoot || resolvedTarget.startsWith(resolvedRoot + path.sep);
}
function resolveExistingAssetDirs(pluginRoot: string, dirname: 'skills' | 'commands'): string[] {
const dir = path.join(pluginRoot, dirname);
try {
return fs.statSync(dir).isDirectory() ? [dir] : [];
} catch {
return [];
}
}
async function importPluginModule(entryPath: string): Promise<MastraCodePlugin> {
if (path.extname(entryPath) !== '.ts') {
throw new Error(
`Unsupported plugin entry extension "${path.extname(entryPath)}". V1 plugins must use .ts entries.`,
);
}
const url = pathToFileURL(entryPath);
const stat = fs.statSync(entryPath, { bigint: true });
url.searchParams.set('mtimeNs', stat.mtimeNs.toString());
url.searchParams.set('size', stat.size.toString());
const mod = (await import(url.href)) as { default?: unknown; plugin?: unknown };
return validatePluginExport(mod.default ?? mod.plugin);
}
function validatePluginExport(value: unknown): MastraCodePlugin {
if (!value || typeof value !== 'object') {
throw new Error('Plugin module must export a plugin object as default or named "plugin" export');
}
View on GitHub (pinned to 75dd419e61)
Solutions
- Point record.entry at the plugin's TypeScript source file, e.g. 'src/index.ts'.
- Rename the entry to .ts and convert the module to TypeScript (the loader imports TS directly).
- Update the stale plugins.json entry if it still references an old .js build output.
Example fix
// before (plugins.json)
{ "id": "widgets", "entry": "dist/index.js", ... }
// after
{ "id": "widgets", "entry": "src/index.ts", ... } Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
function isTsEntry(entry: string): boolean {
return path.extname(entry) === '.ts';
}
if (!isTsEntry(record.entry)) throw new Error(`V1 plugins require a .ts entry, got "${record.entry}"`); Try / catch
const loaded = await loadPluginRecord(record, options);
if (loaded.status === 'load failed' && loaded.error?.includes('Unsupported plugin entry extension')) {
console.error(`Point record.entry at the .ts source (e.g. src/index.ts), not a JS build output.`);
} Prevention
- Set record.entry to the TypeScript source file, not dist/*.js output.
- Convert JS plugins to .ts entry modules before installing.
- Validate entry extension === '.ts' when generating registry records.
When it happens
Trigger: record.entry pointing at 'dist/plugin.js' or 'index.mjs' (e.g. after building the plugin or porting to JS); a JSON entry; a plugin package that ships only compiled JS.
Common situations: Migrating an existing JS plugin package into mastracode; pointing the entry at a build output directory out of habit from npm-package conventions; typos giving the file a wrong extension.
Related errors
- Plugin module must export a plugin object as default or name
- ${label} must be a string.
- Invalid MemoryRequestContext.thread.id: expected string, got
- Invalid input: expected StepParams, Agent, ToolStep, or Proc
- observationalMemory.experimental_subconscious must be a Subc
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c9b9dd148a0ff1fe.
Report an issue: GitHub.