jackwener/OpenCLI · error

Updated sub-plugin "${pluginName}" is invalid: - ${validatio

Error message

Updated sub-plugin "${pluginName}" is invalid:
- ${validation.errors.join('
- ')}

What it means

As the final gate of a monorepo update, opencli runs validatePluginStructure against each updated sub-plugin's directory inside the freshly cloned repo (resolved via resolveRepoContainedPath). If the new checkout fails structural validation, the update aborts and lists each validation error. This prevents replacing a working installed sub-plugin with a broken upstream revision.

Source

Thrown at src/plugin.ts:995

    name: string;
    lockEntry: LockEntry;
    manifestEntry: NonNullable<PluginManifest['plugins']>[string];
  }> = [];

  for (const [pluginName, entry] of Object.entries(lock)) {
    if (entry.source.kind !== 'monorepo' || entry.source.repoName !== monoName) continue;
    const manifestEntry = manifest.plugins?.[pluginName];
    if (!manifestEntry || manifestEntry.disabled) {
      throw new Error(`Installed sub-plugin "${pluginName}" no longer exists in ${cloneUrl}`);
    }
    if (manifestEntry.opencli && !checkCompatibility(manifestEntry.opencli)) {
      throw new Error(`Sub-plugin "${pluginName}" requires opencli ${manifestEntry.opencli}`);
    }

    const subDir = resolveRepoContainedPath(tmpCloneDir, manifestEntry.path);
    const validation = validatePluginStructure(subDir);
    if (!validation.valid) {
      throw new Error(`Updated sub-plugin "${pluginName}" is invalid:\n- ${validation.errors.join('\n- ')}`);
    }
    updatedPlugins.push({ name: pluginName, lockEntry: entry, manifestEntry });
  }

  return updatedPlugins;
}

function updateMonorepoLockEntries(
  lock: Record<string, LockEntry>,
  plugins: Array<{
    name: string;
    lockEntry: LockEntry;
    manifestEntry: NonNullable<PluginManifest['plugins']>[string];
  }>,
  cloneUrl: string,
  monoName: string,
  commitHash: string | undefined,
): void {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the bullet-listed errors and fix upstream (or use a repo revision where the sub-plugin is valid).
  2. Pin the update to the last known-good commit/tag of the monorepo.
  3. Verify manifestEntry.path in the upstream manifest matches the actual package directory containing the manifest.
  4. Retry after clearing temp clone state in case of an incomplete clone.

Example fix

// before (manifest path stale after rename)
"lint": { "path": "packages/lint" }   // packages/lint no longer exists
// after
"lint": { "path": "packages/eslint-plugin" }
Defensive patterns

Strategy: validation

Validate before calling

const subDir = path.join(tmpCloneDir, manifestEntry.path);
if (!fs.existsSync(path.join(subDir, 'opencli.json'))) {
  throw new Error(`Updated sub-plugin at ${subDir} has no manifest; upstream revision is broken`);
}

Type guard

function dirHasManifest(base: string, relPath: string): boolean {
  try { return fs.statSync(path.join(base, relPath, 'opencli.json')).isFile(); } catch { return false; }
}

Try / catch

try {
  await opencli.plugin.updateMonorepo(monoName);
} catch (e) {
  if (e.message.includes('is invalid')) {
    console.error('Pin the monorepo to a known-good revision or fix upstream, then retry:', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Updating a monorepo when the upstream revision has a broken sub-plugin: missing manifest or entry files under manifestEntry.path, wrong path in the manifest, or incomplete clone contents in tmpCloneDir.

Common situations: Upstream published a broken commit; manifestEntry.path points to a renamed/moved package directory; shallow/partial clone missing files; path separators or case mismatches on different OSes.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/d187863510b2de2b. Report an issue: GitHub.