Skip to content

Commit ea908ac

Browse files
committed
fix(config): improve missing channel plugin diagnostics
1 parent 8ee08b2 commit ea908ac

3 files changed

Lines changed: 69 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Docs: https://docs.openclaw.ai
5555
### Fixes
5656

5757
- Agents/OpenAI: default direct OpenAI Responses models to the SSE transport instead of WebSocket auto-selection, preventing pi runtime chat turns from hanging on servers where the WebSocket path stalls while the OpenAI HTTP stream works. Thanks @vincentkoc.
58+
- Doctor/config: report missing catalog-backed channel plugin entries as repairable installs, so upgrades with Discord or WhatsApp config point to `openclaw doctor --fix` instead of telling users to remove valid plugin config. Fixes #77483.
5859
- CLI/update: disable and skip plugins that fail package-update plugin sync, so a broken npm/ClawHub/git/marketplace plugin cannot turn a successful OpenClaw package update into a failed update result. Thanks @vincentkoc.
5960
- CLI/update: use an absolute POSIX npm script shell during package-manager updates, so restricted PATH environments can still run dependency lifecycle scripts while updating from `--tag main`. Fixes #77530. Thanks @PeterTremonti.
6061
- Diagnostics: grant the internal diagnostics event bus to official installed diagnostics exporter plugins, so npm-installed `@openclaw/diagnostics-prometheus` can emit metrics without broadening the capability to arbitrary global plugins. Fixes #76628. Thanks @RayWoo.

src/config/config.plugin-validation.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,45 @@ describe("config plugin validation", () => {
497497
});
498498
});
499499

500+
it("points missing installable plugin entries at doctor repair", () => {
501+
const res = validateConfigObjectWithPlugins(
502+
{
503+
agents: { list: [{ id: "pi" }] },
504+
channels: {
505+
discord: { token: "xxx" },
506+
whatsapp: {},
507+
},
508+
plugins: {
509+
entries: {
510+
discord: { enabled: true },
511+
whatsapp: { enabled: true },
512+
},
513+
},
514+
},
515+
{
516+
env: suiteEnv(),
517+
pluginMetadataSnapshot: {
518+
manifestRegistry: {
519+
plugins: [],
520+
diagnostics: [],
521+
},
522+
},
523+
},
524+
);
525+
526+
expect(res.ok).toBe(true);
527+
expect(res.warnings ?? []).toContainEqual({
528+
path: "plugins.entries.discord",
529+
message:
530+
"plugin not installed: discord (Discord; run openclaw doctor --fix or openclaw plugins install @openclaw/discord; config preserved)",
531+
});
532+
expect(res.warnings ?? []).toContainEqual({
533+
path: "plugins.entries.whatsapp",
534+
message:
535+
"plugin not installed: whatsapp (WhatsApp; run openclaw doctor --fix or openclaw plugins install @openclaw/whatsapp; config preserved)",
536+
});
537+
});
538+
500539
it("uses persisted installed-plugin records as stale channel evidence", async () => {
501540
const installedPluginIndexPath = path.join(suiteHome, ".openclaw", "plugins", "installs.json");
502541
await mkdirSafe(path.dirname(installedPluginIndexPath));

src/config/validation.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import {
1212
import { loadInstalledPluginIndexInstallRecordsSync } from "../plugins/installed-plugin-index-record-reader.js";
1313
import { resolveManifestCommandAliasOwnerInRegistry } from "../plugins/manifest-command-aliases.js";
1414
import type { PluginManifestRegistry } from "../plugins/manifest-registry.js";
15+
import {
16+
getOfficialExternalPluginCatalogEntry,
17+
resolveOfficialExternalPluginInstall,
18+
resolveOfficialExternalPluginLabel,
19+
} from "../plugins/official-external-plugin-catalog.js";
1520
import {
1621
loadPluginMetadataSnapshot,
1722
type PluginMetadataSnapshot,
@@ -1473,10 +1478,23 @@ function validateConfigObjectWithPluginsBase(
14731478
blockedDiagnosticSourceMatchesPluginId(diagnostic, pluginId),
14741479
);
14751480
};
1481+
const formatInstallablePluginWarning = (pluginId: string): string | null => {
1482+
const catalogEntry = getOfficialExternalPluginCatalogEntry(pluginId);
1483+
if (!catalogEntry) {
1484+
return null;
1485+
}
1486+
const install = resolveOfficialExternalPluginInstall(catalogEntry);
1487+
const installSpec = install?.npmSpec ?? install?.clawhubSpec;
1488+
if (!installSpec) {
1489+
return null;
1490+
}
1491+
const label = resolveOfficialExternalPluginLabel(catalogEntry);
1492+
return `plugin not installed: ${pluginId} (${label}; run openclaw doctor --fix or openclaw plugins install ${installSpec}; config preserved)`;
1493+
};
14761494
const pushMissingPluginIssue = (
14771495
path: string,
14781496
pluginId: string,
1479-
opts?: { warnOnly?: boolean },
1497+
opts?: { warnOnly?: boolean; doctorRepairable?: boolean },
14801498
) => {
14811499
if (LEGACY_REMOVED_PLUGIN_IDS.has(pluginId)) {
14821500
warnings.push({
@@ -1497,9 +1515,14 @@ function validateConfigObjectWithPluginsBase(
14971515
return;
14981516
}
14991517
if (opts?.warnOnly) {
1518+
const installablePluginWarning = opts.doctorRepairable
1519+
? formatInstallablePluginWarning(pluginId)
1520+
: null;
15001521
warnings.push({
15011522
path,
1502-
message: `plugin not found: ${pluginId} (stale config entry ignored; remove it from plugins config)`,
1523+
message:
1524+
installablePluginWarning ??
1525+
`plugin not found: ${pluginId} (stale config entry ignored; remove it from plugins config)`,
15031526
});
15041527
return;
15051528
}
@@ -1516,7 +1539,10 @@ function validateConfigObjectWithPluginsBase(
15161539
for (const pluginId of Object.keys(entries)) {
15171540
if (!knownIds.has(pluginId)) {
15181541
// Keep gateway startup resilient when plugins are removed/renamed across upgrades.
1519-
pushMissingPluginIssue(`plugins.entries.${pluginId}`, pluginId, { warnOnly: true });
1542+
pushMissingPluginIssue(`plugins.entries.${pluginId}`, pluginId, {
1543+
warnOnly: true,
1544+
doctorRepairable: true,
1545+
});
15201546
}
15211547
}
15221548
}

0 commit comments

Comments
 (0)