{"record":{"id":"84810b306de43a22","repo":"jackwener/OpenCLI","slug":"plugin-name-structure-invalid-n-validatio","errorCode":null,"errorMessage":"Plugin \"${name}\" structure invalid:\\n- ${validation.errors.join('\\n- ')}","messagePattern":"Plugin \"(.+?)\" structure invalid:\\\\n- (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/plugin.ts","lineNumber":831,"sourceCode":"    source: { kind: 'local', path: resolvedPath },\n    commitHash: commitHash ?? 'local',\n  });\n  writeLockFile(lock);\n\n  return pluginName;\n}\n\nfunction updateLocalPlugin(\n  name: string,\n  targetDir: string,\n  lock: Record<string, LockEntry>,\n  lockEntry?: LockEntry,\n): void {\n  const pluginDir = fs.realpathSync(targetDir);\n\n  const validation = validatePluginStructure(pluginDir);\n  if (!validation.valid) {\n    log.warn(`Plugin \"${name}\" structure invalid:\\n- ${validation.errors.join('\\n- ')}`);\n  }\n\n  postInstallLifecycle(pluginDir);\n\n  upsertLockEntry(lock, name, {\n    source: lockEntry?.source ?? { kind: 'local', path: pluginDir },\n    commitHash: getCommitHash(pluginDir) ?? 'local',\n    installedAt: lockEntry?.installedAt ?? new Date().toISOString(),\n    updatedAt: new Date().toISOString(),\n  });\n  writeLockFile(lock);\n}\n\n/** Install sub-plugins from a monorepo. */\nfunction installMonorepo(\n  cloneDir: string,\n  cloneUrl: string,\n  repoName: string,","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/plugin.ts#L813-L849","documentation":"During plugin installation (installFromLocal-style flow), validatePluginStructure checks the resolved plugin directory for required files/layout (plugin.json, entry points, etc.). If validation fails, this warning lists each structural problem; installation continues (postInstallLifecycle and lock entry upsert still run), so the plugin is registered despite being structurally suspect.","triggerScenarios":"validatePluginStructure(pluginDir) returns { valid: false, errors: [...] } — missing plugin manifest, missing declared entry file, wrong directory layout, or unreadable manifest JSON — right after fs.realpathSync(targetDir) resolves the install target.","commonSituations":"Hand-writing a plugin and forgetting plugin.json or the main entry; renaming files referenced by the manifest; installing a directory that only contains source without build output; manifest JSON with a syntax error.","solutions":["Read the listed validation errors — they name exactly which files/fields are missing or wrong.","Create or fix the plugin manifest (plugin.json) with correct name/main fields.","Build the plugin if entry points point to unbuilt TS/dist output.","Validate JSON syntax of the manifest with a linter before reinstalling."],"exampleFix":"// before\nmy-plugin/\n  index.ts      (no manifest)\n// after\nmy-plugin/\n  plugin.json   { \"name\": \"my-plugin\", \"main\": \"dist/index.js\" }\n  dist/index.js","handlingStrategy":"validation","validationCode":"const manifest = JSON.parse(fs.readFileSync(path.join(pluginDir, 'plugin.json'), 'utf-8'));\nif (!manifest.name || !manifest.main) throw new Error('plugin.json missing name/main');\nif (!fs.existsSync(path.join(pluginDir, manifest.main))) throw new Error(`entry not found: ${manifest.main}`);","typeGuard":"function isPluginDirValid(dir: string): boolean {\n  try {\n    const m = JSON.parse(fs.readFileSync(path.join(dir, 'plugin.json'), 'utf-8'));\n    return typeof m.name === 'string' && typeof m.main === 'string' && fs.existsSync(path.join(dir, m.main));\n  } catch { return false; }\n}","tryCatchPattern":"const validation = validatePluginStructure(pluginDir);\nif (!validation.valid) {\n  validation.errors.forEach((e) => console.error(`plugin structure: ${e}`));\n  process.exitCode = 1; // fail fast instead of installing a broken plugin\n}","preventionTips":["Always ship plugin.json with correct name and main fields.","Build TS plugins to the entry path the manifest declares.","Lint manifest JSON before packaging.","Run validatePluginStructure in CI before publishing a plugin."],"tags":["plugin","validation","structure"],"backgroundTag":"plugin-structure-invalid","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}