diff --git a/src/config/config.plugin-validation.test.ts b/src/config/config.plugin-validation.test.ts index 62584f138..02542eac3 100644 --- a/src/config/config.plugin-validation.test.ts +++ b/src/config/config.plugin-validation.test.ts @@ -65,17 +65,18 @@ describe("config plugin validation", () => { } }); - it("rejects missing plugin ids in entries", async () => { + it("warns for missing plugin ids in entries instead of failing validation", async () => { const home = await createCaseHome(); const res = validateInHome(home, { agents: { list: [{ id: "pi" }] }, plugins: { enabled: false, entries: { "missing-plugin": { enabled: true } } }, }); - expect(res.ok).toBe(false); - if (!res.ok) { - expect(res.issues).toContainEqual({ + expect(res.ok).toBe(true); + if (res.ok) { + expect(res.warnings).toContainEqual({ path: "plugins.entries.missing-plugin", - message: "plugin not found: missing-plugin", + message: + "plugin not found: missing-plugin (stale config entry ignored; remove it from plugins config)", }); } }); diff --git a/src/config/validation.ts b/src/config/validation.ts index 746f89ef0..fab635125 100644 --- a/src/config/validation.ts +++ b/src/config/validation.ts @@ -315,7 +315,11 @@ function validateConfigObjectWithPluginsBase( } const { registry, knownIds, normalizedPlugins } = ensureRegistry(); - const pushMissingPluginIssue = (path: string, pluginId: string) => { + const pushMissingPluginIssue = ( + path: string, + pluginId: string, + opts?: { warnOnly?: boolean }, + ) => { if (LEGACY_REMOVED_PLUGIN_IDS.has(pluginId)) { warnings.push({ path, @@ -323,6 +327,13 @@ function validateConfigObjectWithPluginsBase( }); return; } + if (opts?.warnOnly) { + warnings.push({ + path, + message: `plugin not found: ${pluginId} (stale config entry ignored; remove it from plugins config)`, + }); + return; + } issues.push({ path, message: `plugin not found: ${pluginId}`, @@ -335,7 +346,8 @@ function validateConfigObjectWithPluginsBase( if (entries && isRecord(entries)) { for (const pluginId of Object.keys(entries)) { if (!knownIds.has(pluginId)) { - pushMissingPluginIssue(`plugins.entries.${pluginId}`, pluginId); + // Keep gateway startup resilient when plugins are removed/renamed across upgrades. + pushMissingPluginIssue(`plugins.entries.${pluginId}`, pluginId, { warnOnly: true }); } } }