fix(config): warn and ignore unknown plugin entry keys

Prevent gateway startup failures when plugins.entries contains stale or removed plugin ids by downgrading unknown entry keys from validation errors to warnings.

Made-with: Cursor
(cherry picked from commit 34ef28cf63564159f3144cfb3b38756b468af4f0)
This commit is contained in:
SidQin-cyber
2026-02-26 20:21:36 +08:00
committed by Peter Steinberger
parent 1ba525f94d
commit a481ed00f5
2 changed files with 20 additions and 7 deletions

View File

@@ -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)",
});
}
});

View File

@@ -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 });
}
}
}