jackwener/OpenCLI · warning
Plugin "${name}" structure invalid:\n- ${validation.errors.j
Error message
Plugin "${name}" structure invalid:\n- ${validation.errors.join('\n- ')} What it means
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.
Source
Thrown at src/plugin.ts:831
source: { kind: 'local', path: resolvedPath },
commitHash: commitHash ?? 'local',
});
writeLockFile(lock);
return pluginName;
}
function updateLocalPlugin(
name: string,
targetDir: string,
lock: Record<string, LockEntry>,
lockEntry?: LockEntry,
): void {
const pluginDir = fs.realpathSync(targetDir);
const validation = validatePluginStructure(pluginDir);
if (!validation.valid) {
log.warn(`Plugin "${name}" structure invalid:\n- ${validation.errors.join('\n- ')}`);
}
postInstallLifecycle(pluginDir);
upsertLockEntry(lock, name, {
source: lockEntry?.source ?? { kind: 'local', path: pluginDir },
commitHash: getCommitHash(pluginDir) ?? 'local',
installedAt: lockEntry?.installedAt ?? new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
writeLockFile(lock);
}
/** Install sub-plugins from a monorepo. */
function installMonorepo(
cloneDir: string,
cloneUrl: string,
repoName: string,View on GitHub (pinned to 49907e53dc)
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.
Example fix
// before
my-plugin/
index.ts (no manifest)
// after
my-plugin/
plugin.json { "name": "my-plugin", "main": "dist/index.js" }
dist/index.js Defensive patterns
Strategy: validation
Validate before calling
const manifest = JSON.parse(fs.readFileSync(path.join(pluginDir, 'plugin.json'), 'utf-8'));
if (!manifest.name || !manifest.main) throw new Error('plugin.json missing name/main');
if (!fs.existsSync(path.join(pluginDir, manifest.main))) throw new Error(`entry not found: ${manifest.main}`); Type guard
function isPluginDirValid(dir: string): boolean {
try {
const m = JSON.parse(fs.readFileSync(path.join(dir, 'plugin.json'), 'utf-8'));
return typeof m.name === 'string' && typeof m.main === 'string' && fs.existsSync(path.join(dir, m.main));
} catch { return false; }
} Try / catch
const validation = validatePluginStructure(pluginDir);
if (!validation.valid) {
validation.errors.forEach((e) => console.error(`plugin structure: ${e}`));
process.exitCode = 1; // fail fast instead of installing a broken plugin
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Updated sub-plugin "${pluginName}" is invalid: - ${validatio
- Skipping "${name}": invalid structure — ${validation.errors.
- Invalid plugin name "${name}". Plugin names must start with
- Directory "${targetDir}" already exists and is not empty.
- Local plugin path is not a directory: ${localPath}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/84810b306de43a22.
Report an issue: GitHub.