{"record":{"id":"79796fd7aa8cfffc","repo":"mastra-ai/mastra","slug":"plugin-entry-must-be-a-ts-file","errorCode":null,"errorMessage":"Plugin entry must be a .ts file","messagePattern":"Plugin entry must be a \\.ts file","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mastracode/sdk/src/plugins/install.ts","lineNumber":163,"sourceCode":"    return detectEntry(pluginDir);\n  } catch {\n    return undefined;\n  }\n}\n\nexport function detectEntry(pluginDir: string, explicitEntry?: string): string {\n  const root = path.resolve(pluginDir);\n  if (explicitEntry) {\n    const entryPath = path.resolve(pluginDir, explicitEntry);\n    if (!isInsideDirectory(entryPath, root)) {\n      throw new Error('Plugin entry must be inside the plugin directory');\n    }\n    if (fs.existsSync(entryPath) && fs.statSync(entryPath).isDirectory()) {\n      const nestedEntry = detectEntry(entryPath);\n      return path.relative(root, path.join(entryPath, nestedEntry));\n    }\n    if (path.extname(entryPath) !== '.ts') {\n      throw new Error('Plugin entry must be a .ts file');\n    }\n    if (!fs.existsSync(entryPath) || !fs.statSync(entryPath).isFile()) {\n      throw new Error(`Plugin entry file does not exist: ${explicitEntry}`);\n    }\n    return path.relative(root, entryPath);\n  }\n\n  const manifestPlugin = getSingleManifestPlugin(pluginDir);\n  if (manifestPlugin) {\n    return detectEntry(pluginDir, manifestPlugin.entry);\n  }\n\n  for (const candidate of ENTRY_CANDIDATES) {\n    const entryPath = path.join(pluginDir, candidate);\n    if (fs.existsSync(entryPath) && fs.statSync(entryPath).isFile()) {\n      return candidate;\n    }\n  }","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/sdk/src/plugins/install.ts#L145-L181","documentation":"detectEntry requires explicit plugin entry files to be TypeScript (`.ts`) since plugins are loaded from TS source. This throw fires when the explicit entry has a different extension (e.g. `.js`, `.mjs`, or no extension treated as a file).","triggerScenarios":"Calling entry/detectEntry with explicitEntry whose resolved path has path.extname !== '.ts' and is not a directory, e.g. `entry: 'index.js'` or `entry: 'main.mjs'`.","commonSituations":"Authors porting a JS plugin; pointing at a compiled `dist/index.js`; omitting the extension on a file that is not a `.ts` candidate.","solutions":["Point the entry at the TypeScript source file, e.g. `entry: 'src/index.ts'`.","Rename/convert the entry to TypeScript if the plugin is authored in JS.","Reference the `.ts` source, not a compiled `dist/*.js` output."],"exampleFix":"// before\n{ entry: 'dist/index.js' }\n\n// after\n{ entry: 'src/index.ts' }","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nexport function assertTsEntry(entry: string): void {\n  if (path.extname(entry) !== '.ts') {\n    throw new Error(`Plugin entry must be a .ts file, got: ${entry}`);\n  }\n}","typeGuard":"function isTsEntry(entry: string): boolean {\n  return entry.endsWith('.ts') && !entry.endsWith('.d.ts');\n}","tryCatchPattern":null,"preventionTips":["Author plugin entries in TypeScript; treat dist/*.js output as non-installable.","Standardize entries as src/index.ts across plugin repos.","Check the extension before saving `entry` in config."],"tags":["plugins","typescript","configuration"],"backgroundTag":"invalid-entry-file-extension","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}