← Back to PRs

#21871: fix(plugins): suppress false duplicate warning when user-installed plugin shadows bundled

by hydro13 open 2026-02-20 13:39 View on GitHub →
docs size: S
## Summary When a user installs a plugin (e.g. `nextcloud-talk`) via `openclaw plugins nextcloud-talk install`, the registry emitted a spurious `duplicate plugin id detected` warning. The plugin was working correctly — the user-installed copy was winning over the bundled copy as expected — but the warning was alarming and logged on every startup. ## Root Cause The duplicate-detection branch in `loadPluginManifestRegistry` handled two cases: 1. **Same physical path** (via symlinks/aliases) → silent skip ✅ 2. **Different paths** → always warn ❌ Case 2 incorrectly treated "user-installed plugin shadows bundled copy" the same as a genuine conflict between two non-bundled plugins. The former is expected behavior; only the latter deserves a warning. ## Fix Extend the silent-preference path to also cover the case where exactly one of the two candidates has `bundled` origin (XOR). The higher-precedence copy (`config > workspace > global > bundled`) wins silently without a warning. A warning still fires when: - Two **non-bundled** plugins claim the same ID (genuine conflict) - Two **bundled** plugins at different paths share an ID (shouldn't happen in practice) ```typescript // Before: only samePlugin was silent if (samePlugin) { ... } diagnostics.push({ level: "warn", ... }); // ← fired for user-install vs bundled // After: samePlugin OR exactly one bundled → silent const existingIsBundled = existing.candidate.origin === "bundled"; const candidateIsBundled = candidate.origin === "bundled"; const oneBundled = existingIsBundled !== candidateIsBundled; if (samePlugin || oneBundled) { ... } diagnostics.push({ level: "warn", ... }); // ← only fires for genuine conflicts ``` ## Tests Updated + added 4 test cases in `manifest-registry.test.ts`: | Scenario | Result | |---|---| | `global` + `workspace` same ID (non-bundled conflict) | ✅ warning fires | | `global` shadows `bundled` same ID | ✅ no warning, global wins | | `config` shadows `bundled` same ID | ✅ no warning, config wins | | two `bundled` at different paths same ID | ✅ warning fires | | existing symlink / same-path tests | ✅ unchanged, still pass | All 7 tests pass. Fixes #21714 <!-- greptile_comment --> <h3>Greptile Summary</h3> Fixed spurious duplicate plugin warning when user-installed plugins shadow bundled versions. The change introduces XOR logic to detect when exactly one plugin is bundled, allowing the higher-precedence user-installed copy to silently win. Warnings now only fire for genuine conflicts between non-bundled plugins or between multiple bundled plugins at different paths. - Modified duplicate detection in `loadPluginManifestRegistry` to distinguish between expected shadowing (user vs bundled) and genuine conflicts - Added four comprehensive test cases covering non-bundled conflicts, user-shadowing-bundled scenarios, and bundled-bundled conflicts - Updated existing test to use non-bundled origins (global vs workspace) since bundled vs global no longer warns <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no risk - The implementation is straightforward, well-tested, and addresses a specific false-positive warning. The XOR logic correctly distinguishes between expected shadowing (user-installed vs bundled) and genuine conflicts. All edge cases are covered by comprehensive tests, and the change is minimal and focused. - No files require special attention <sub>Last reviewed commit: c38c2f2</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs