docs(doctor): clarify configured plugin repair (#77613)

This commit is contained in:
Vincent Koc
2026-05-04 18:16:29 -07:00
committed by GitHub
parent b062bb670d
commit 0131343db8
12 changed files with 19 additions and 18 deletions

View File

@@ -45,7 +45,7 @@ Notes:
- State integrity checks now detect orphan transcript files in the sessions directory. Archiving them as `.deleted.<timestamp>` requires an interactive confirmation; `--fix`, `--yes`, and headless runs leave them in place.
- Doctor also scans `~/.openclaw/cron/jobs.json` (or `cron.store`) for legacy cron job shapes and can rewrite them in place before the scheduler has to auto-normalize them at runtime.
- On Linux, doctor warns when the user's crontab still runs legacy `~/.openclaw/bin/ensure-whatsapp.sh`; that script is no longer maintained and can log false WhatsApp gateway outages when cron lacks the systemd user-bus environment.
- Doctor cleans legacy plugin dependency staging state created by older OpenClaw versions. It also repairs missing configured downloadable plugins when the registry can resolve them, and the 2026.5.2 doctor pass automatically installs downloadable plugins that an older config already uses before marking the config touched for that release. If the download fails, doctor reports the install error and preserves the configured plugin entry for the next repair attempt.
- Doctor cleans legacy plugin dependency staging state created by older OpenClaw versions. It also repairs missing downloadable plugins that are referenced by config, such as `plugins.entries`, configured channels, configured provider/search settings, or configured agent runtimes. During package updates, doctor skips package-manager plugin repair until the package swap is complete; rerun `openclaw doctor --fix` afterward if a configured plugin still needs recovery. If the download fails, doctor reports the install error and preserves the configured plugin entry for the next repair attempt.
- Doctor repairs stale plugin config by removing missing plugin ids from `plugins.allow`/`plugins.entries`, plus matching dangling channel config, heartbeat targets, and channel model overrides when plugin discovery is healthy.
- Doctor quarantines invalid plugin config by disabling the affected `plugins.entries.<id>` entry and removing its invalid `config` payload. Gateway startup already skips only that bad plugin so other plugins and channels can keep running.
- Set `OPENCLAW_SERVICE_REPAIR_POLICY=external` when another supervisor owns the gateway lifecycle. Doctor still reports gateway/service health and applies non-service repairs, but skips service install/start/restart/bootstrap and legacy service cleanup.

View File

@@ -266,7 +266,7 @@ directory remains inert so normal packaged installs still use compiled dist.
For runtime hook debugging:
- `openclaw plugins inspect <id> --runtime --json` shows registered hooks and diagnostics from a module-loaded inspection pass. Runtime inspection never installs dependencies; use `openclaw doctor --fix` to clean legacy dependency state or install missing configured downloadable plugins.
- `openclaw plugins inspect <id> --runtime --json` shows registered hooks and diagnostics from a module-loaded inspection pass. Runtime inspection never installs dependencies; use `openclaw doctor --fix` to clean legacy dependency state or recover missing downloadable plugins that are referenced by config.
- `openclaw gateway status --deep --require-rpc` confirms the reachable Gateway, service/process hints, config path, and RPC health.
- Non-bundled conversation hooks (`llm_input`, `llm_output`, `before_agent_finalize`, `agent_end`) require `plugins.entries.<id>.hooks.allowConversationAccess=true`.

View File

@@ -355,7 +355,7 @@ That stages grounded durable candidates into the short-term dreaming store while
<Accordion title="7b. Plugin install cleanup">
Doctor removes legacy OpenClaw-generated plugin dependency staging state in `openclaw doctor --fix` / `openclaw doctor --repair` mode. This covers stale generated dependency roots, old install-stage directories, package-local debris from earlier bundled-plugin dependency repair code, and orphaned or recovered managed npm copies of bundled `@openclaw/*` plugins that can shadow the current bundled manifest.
Doctor can also reinstall configured downloadable plugins when the config references them but the local plugin registry cannot find them. For the 2026.5.2 bundled-plugin externalization, doctor automatically installs downloadable plugins that the existing config already uses and then relies on `meta.lastTouchedVersion` to run that release pass only once. Gateway startup and config reload do not run package managers; plugin installs remain explicit doctor/install/update work.
Doctor can also reinstall missing downloadable plugins when config references them but the local plugin registry cannot find them. Examples include material `plugins.entries`, configured channel/provider/search settings, and configured agent runtimes. During package updates, doctor avoids running package-manager plugin repair while the core package is being swapped; run `openclaw doctor --fix` again after the update if a configured plugin still needs recovery. Gateway startup and config reload do not run package managers; plugin installs remain explicit doctor/install/update work.
</Accordion>
<Accordion title="8. Gateway service migrations and cleanup hints">

View File

@@ -262,8 +262,8 @@ dual-format packages from being partially installed as bundles.
downloadable through the plugin installer. Gateway startup never runs a
package manager for them.
- `openclaw doctor --fix` removes legacy staged dependency directories and can
install configured downloadable plugins that are missing from the local
plugin index.
recover downloadable plugins that are missing from the local plugin index when
config references them.
## Security

View File

@@ -85,9 +85,10 @@ openclaw plugins install <source>
openclaw doctor --fix
```
`doctor --fix` can clean legacy OpenClaw-generated dependency state and install
configured downloadable plugins that are missing from the local install records.
It does not repair dependencies for an already-installed local plugin.
`doctor --fix` can clean legacy OpenClaw-generated dependency state and recover
downloadable plugins that are missing from the local install records when config
references them. Doctor does not repair dependencies for an already-installed
local plugin.
## Bundled plugins

View File

@@ -363,7 +363,7 @@ describe("doctor repair sequencing", () => {
it("does not remove deferred configured plugins during the package update doctor pass", async () => {
mocks.repairMissingConfiguredPluginInstalls.mockResolvedValueOnce({
changes: [
'Deferred missing configured plugin "brave" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "brave" during package update; rerun "openclaw doctor --fix" after the update completes.',
],
warnings: [],
});
@@ -432,7 +432,7 @@ describe("doctor repair sequencing", () => {
expect(result.state.candidate.plugins?.allow).toEqual(["brave"]);
expect(result.state.candidate.plugins?.entries?.brave?.enabled).toBe(true);
expect(result.changeNotes).toContain(
'Deferred missing configured plugin "brave" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "brave" during package update; rerun "openclaw doctor --fix" after the update completes.',
);
});

View File

@@ -917,7 +917,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
);
expect(result).toEqual({
changes: [
'Deferred missing configured plugin "discord" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "discord" during package update; rerun "openclaw doctor --fix" after the update completes.',
],
warnings: [],
});
@@ -969,7 +969,7 @@ describe("repairMissingConfiguredPluginInstalls", () => {
);
expect(result).toEqual({
changes: [
'Deferred missing configured plugin "discord" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "discord" during package update; rerun "openclaw doctor --fix" after the update completes.',
],
warnings: [],
});

View File

@@ -676,7 +676,7 @@ async function repairMissingPluginInstalls(params: {
}
delete nextRecords[pluginId];
changes.push(
`Deferred missing configured plugin "${pluginId}" install repair until post-update doctor.`,
`Skipped package-manager repair for configured plugin "${pluginId}" during package update; rerun "openclaw doctor --fix" after the update completes.`,
);
}
}

View File

@@ -284,7 +284,7 @@ describe("configured plugin install release step", () => {
it("does not stamp config during update-time deferred install repair", async () => {
mocks.repairMissingPluginInstallsForIds.mockResolvedValue({
changes: [
'Deferred missing configured plugin "codex" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "codex" during package update; rerun "openclaw doctor --fix" after the update completes.',
],
warnings: [],
});
@@ -313,7 +313,7 @@ describe("configured plugin install release step", () => {
);
expect(result).toEqual({
changes: [
'Deferred missing configured plugin "codex" install repair until post-update doctor.',
'Skipped package-manager repair for configured plugin "codex" during package update; rerun "openclaw doctor --fix" after the update completes.',
],
warnings: [],
completed: false,

View File

@@ -650,7 +650,7 @@ export function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
}),
createDoctorHealthContribution({
id: "doctor:release-configured-plugin-installs",
label: "Configured plugin installs",
label: "Configured plugin repair",
run: runReleaseConfiguredPluginInstallsHealth,
}),
createDoctorHealthContribution({

View File

@@ -651,7 +651,7 @@ function buildDetachHintSuffix(detachHint?: string): string {
}
export function buildPluginBindingUnavailableText(binding: PluginConversationBinding): string {
return `The bound plugin ${resolvePluginBindingDisplayName(binding)} is not currently loaded. Routing this message to OpenClaw instead.${buildDetachHintSuffix(binding.detachHint)}`;
return `The bound plugin ${resolvePluginBindingDisplayName(binding)} is not currently loaded. Routing this message to OpenClaw instead. If this started after an update, run "openclaw doctor --fix"; otherwise reinstall or enable the plugin.${buildDetachHintSuffix(binding.detachHint)}`;
}
export function buildPluginBindingDeclinedText(binding: PluginConversationBinding): string {

View File

@@ -487,7 +487,7 @@ export async function applyAuthChoicePluginProvider(
const provider = resolveProviderMatch(providers, options.providerId);
if (!provider) {
await params.prompter.note(
`${options.label} auth plugin is not available. Enable it and re-run onboarding.`,
`${options.label} auth plugin is not available. Install or enable the plugin, then rerun onboarding. If this started after an update, run "openclaw doctor --fix" first.`,
options.label,
);
return { config: nextConfig };