{"record":{"id":"e1c498a6491cd696","repo":"jackwener/OpenCLI","slug":"installed-sub-plugin-pluginname-no-longer-exi","errorCode":null,"errorMessage":"Installed sub-plugin \"${pluginName}\" no longer exists in ${cloneUrl}","messagePattern":"Installed sub-plugin \"(.+?)\" no longer exists in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/plugin.ts","lineNumber":986,"sourceCode":"  manifest: PluginManifest,\n  cloneUrl: string,\n  tmpCloneDir: string,\n): Array<{\n  name: string;\n  lockEntry: LockEntry;\n  manifestEntry: NonNullable<PluginManifest['plugins']>[string];\n}> {\n  const updatedPlugins: Array<{\n    name: string;\n    lockEntry: LockEntry;\n    manifestEntry: NonNullable<PluginManifest['plugins']>[string];\n  }> = [];\n\n  for (const [pluginName, entry] of Object.entries(lock)) {\n    if (entry.source.kind !== 'monorepo' || entry.source.repoName !== monoName) continue;\n    const manifestEntry = manifest.plugins?.[pluginName];\n    if (!manifestEntry || manifestEntry.disabled) {\n      throw new Error(`Installed sub-plugin \"${pluginName}\" no longer exists in ${cloneUrl}`);\n    }\n    if (manifestEntry.opencli && !checkCompatibility(manifestEntry.opencli)) {\n      throw new Error(`Sub-plugin \"${pluginName}\" requires opencli ${manifestEntry.opencli}`);\n    }\n\n    const subDir = resolveRepoContainedPath(tmpCloneDir, manifestEntry.path);\n    const validation = validatePluginStructure(subDir);\n    if (!validation.valid) {\n      throw new Error(`Updated sub-plugin \"${pluginName}\" is invalid:\\n- ${validation.errors.join('\\n- ')}`);\n    }\n    updatedPlugins.push({ name: pluginName, lockEntry: entry, manifestEntry });\n  }\n\n  return updatedPlugins;\n}\n\nfunction updateMonorepoLockEntries(\n  lock: Record<string, LockEntry>,","sourceCodeStart":968,"sourceCodeEnd":1004,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/src/plugin.ts#L968-L1004","documentation":"During monorepo plugin update, opencli re-reads the upstream manifest and iterates the installed lock entries belonging to that monorepo. If an installed sub-plugin is missing from the new manifest or is now marked disabled, it throws a plain Error because the update can no longer maintain that sub-plugin. This keeps the lock file consistent with the upstream repo.","triggerScenarios":"Running the monorepo update path after upstream removed a sub-plugin you have installed, or upstream set `disabled: true` for it in manifest.plugins — the lock entry references a pluginName absent (or disabled) in the fresh manifest.","commonSituations":"Repo maintainer deleted or disabled a plugin between install and update; your lock file is stale relative to a heavily refactored upstream manifest; updating on the wrong branch where the plugin doesn't exist.","solutions":["Uninstall the removed/disabled sub-plugin locally so the lock no longer references it, then re-run the update.","If the removal was unintended, open an issue with the monorepo maintainer or pin to the previous commit/tag.","Update your local manifest/config to re-enable the plugin if it was merely disabled and you control the manifest."],"exampleFix":"// before: update fails because 'lint' was removed upstream\nopencli plugin update my-mono\n// after\nopencli plugin uninstall lint\nopencli plugin update my-mono","handlingStrategy":"try-catch","validationCode":"for (const name of Object.keys(lock)) {\n  const entry = lock[name];\n  if (entry.source.kind !== 'monorepo' || entry.source.repoName !== monoName) continue;\n  if (!manifest.plugins?.[name] || manifest.plugins[name].disabled) {\n    console.warn(`${name} was removed/disabled upstream; uninstall before updating`);\n  }\n}","typeGuard":"function stillProvidedUpstream(\n  m: { plugins?: Record<string, { disabled?: boolean }> },\n  name: string,\n): boolean {\n  const e = m.plugins?.[name];\n  return !!e && !e.disabled;\n}","tryCatchPattern":"try {\n  await opencli.plugin.updateMonorepo(monoName);\n} catch (e) {\n  if (e.message.includes('no longer exists in')) {\n    const name = e.message.match(/\"(.*)\"/)?.[1];\n    await opencli.plugin.uninstall(name);\n    await opencli.plugin.updateMonorepo(monoName);\n  } else throw e;\n}","preventionTips":["Read monorepo changelogs before updating.","Clean up lock entries for plugins you no longer use.","Pin monorepo updates to reviewed commits/tags."],"tags":["plugin","monorepo","update","lockfile","drift"],"backgroundTag":"dependency-removed-upstream","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}