Skip to main content
Checks 0-2 cover config normalization and the legacy config key migrations, plus how doctor publishes shared-state schema during an update.

Channel ownership during an update

When Doctor migrates a legacy agents.list roster without a default: true marker to explicit ownership, it also preserves unbound accounts with a binding to the first agent from the old list, which received their implicit traffic before the update. Existing account bindings and narrower conversation routes remain unchanged. Doctor reports each added binding and saves it with the roster migration through the normal config backup and validation flow. Update-channel migration and manual openclaw doctor --fix use the original roster from the config snapshot. A narrower conversation route never establishes account-wide ownership. If the original roster is unavailable, Doctor reports unresolved: original roster unavailable with the exact binding to add and leaves the account’s bindings unchanged. An unresolved account stays blocked with that reason while the Gateway and other accounts continue running; it does not enter a restart loop. Add the reported binding and restart the Gateway.

Missing plugins during migration

A configured plugin that is missing or cannot finish installation does not block Doctor, updates, or Gateway startup. OpenClaw records a warning that names the plugin, its pending migration, and the command to finish installation or repair. The Gateway continues serving the available plugins. doctor --fix repairs an older shared database schema before recording pending plugin migrations. Missing plugins therefore do not prevent the database repair; their inputs stay available for a later retry. Deferred migrations keep their state and legacy config inputs in place. Config repairs can still update unrelated settings, while the pending plugin’s retired fields remain inactive. After installing or repairing the plugin, run openclaw doctor --fix to complete its migration and clear the pending warning. During an update driven by an older version, plugin installation can remain deferred until that updater finishes; its pending inputs receive the same protection. Session edits and deletions made after the core import remain authoritative when the plugin migration resumes. While a migration is pending, explicit config edits that would change or remove its retained inputs are refused with the recovery command. Unrelated settings remain writable. Complete the plugin migration before editing those inputs.

Schema publication during a 2026.9.2 update

When OpenClaw 2026.9.2 drives an update that needs a newer shared-state schema, Doctor applies the migration content and reports schema content applied; version publication deferred until update run <id> finishes. The old updater can finish its ledger access, while the new Gateway uses the migrated content. Publication waits until all affected terminal runs are at least five minutes old; a running row unchanged for more than 30 minutes counts as abandoned. Every writable database open follows this rule, and the Gateway watcher schedules publication after the deadline. Ordinary CLI commands, including Doctor, remain usable while that Gateway runs. Applied content counts as ready; only the owning Gateway, or a writable opener when no Gateway owns the state directory, publishes the version after the grace. Agent-database migrations are not version-deferred. During the published 2026.9.2 updater’s rollback window, Doctor validates private state copies and leaves the live databases and config unchanged. After package rollback is no longer possible, the fresh update continuation requires a verified backup covering each pending agent database and current update ownership before Doctor migrates the live state. Managed updates retain the shipped helper’s original handoff record unchanged. Doctor reports update-schema-bump-unfenced when this handoff cannot be verified, backup coverage is missing, the required shared-state metadata table is absent, or a migration fails. Follow the manual update sequence from the refusal. See Database schemas for the publication contract and the remaining risk for an old CLI stalled beyond the grace period.

Replay a July 2026 config upgrade

From a source checkout with its pnpm dependencies installed, run:
The driver runs with plain Node and imports only Node built-ins. It requires the checkout’s fixture and pnpm openclaw build wrapper; an installed npm package alone cannot run this replay. No tsx invocation is needed for the driver. The replay uses the synthetic test/fixtures/doctor-2026.7.1.json config. It builds through pnpm openclaw, isolates the home, state, config, and logs under .local, and selects free loopback ports. It captures validation before repair, two doctor --fix --non-interactive passes, validation after the first pass, and Gateway startup. It keeps the original config, both repaired copies, and command output in the printed directory. The second pass must leave the config bytes unchanged. It never needs credentials or a running Gateway. A second fixture covers messages.tts moving to tts before retired TTS fields are removed. Doctor preserves the old preference-file path in shared machine state before removing prefsPath; existing canonical settings and stored state keep precedence, and the preferences file stays intact. The fixture combines a two-agent agents.list roster, the legacy model allowlist, local memory search, a CLI audio model, Telegram account allowlists, and the retired meta.lastTouchedAt, gateway.tailscale.resetOnExit, and gateway.nodes.denyCommands keys. The current media field is optional plural capabilities; Doctor adds ["audio"] when moving an audio-only model to tools.media.models. A singular capability field is not required. Local embeddings keep the local provider and their existing model selection. The in-process node-llama-cpp runtime was replaced by a managed llama-server. When managed setup is missing, Doctor and Gateway startup name the degraded semantic recall and the plugin’s guided setup command:
Run that command interactively and choose the appropriate managed setup, then verify with openclaw memory status --deep. Setup can offer embeddings without changing the chat model. Downloads require setup consent. In the July provider, memorySearch.model did not select the local GGUF: local.modelPath did. Doctor therefore preserves both fields instead of silently turning an ignored model value into a different embedding model. See llama.cpp.

Checks 0-2

If this is a git checkout and Doctor is running interactively, it offers to update before running its checks. Accepting uses the normal openclaw update lifecycle for that checkout, including validation, recovery, and Gateway restart. The source update keeps your saved update channel unchanged. Externally managed installs continue Doctor without offering self-update; update them through their deployment owner.
GitHub Copilot now requires explicit provider config, a saved Copilot auth profile, or COPILOT_GITHUB_TOKEN. Generic GH_TOKEN and GITHUB_TOKEN no longer activate it. Doctor reports this change once when only a generic GitHub token is present. The retired plugins.entries.github-copilot.config.discovery.enabled setting is ignored during config loading, including malformed values, and removed when Doctor saves the config.Doctor normalizes legacy value shapes into the current schema. Current Talk speech config is talk.provider + talk.providers.<provider>, with realtime voice config under talk.realtime.*. Doctor rewrites old talk.voiceId / talk.voiceAliases / talk.modelId / talk.outputFormat / talk.apiKey shapes into the provider map, and rewrites legacy top-level realtime selectors (talk.mode, talk.transport, talk.brain, talk.model, talk.voice) into talk.realtime.Doctor also warns when plugins.allow is non-empty and tool policy uses wildcard or plugin-owned tool entries. tools.allow: ["*"] only matches tools from plugins that actually load; it does not bypass the exclusive plugin allowlist.A tool policy scope with nonempty allow and alsoAllow lists fails validation. doctor --fix merges the lists only when the effective profile grants remain unchanged for every agent and provider that inherits the extras. It retains alsoAllow: [] as an explicit override so inherited extras cannot reappear. If the extras may extend a profile or grant Gateway configuration-read access, Doctor leaves the conflicting scope untouched and reports the exact keys and values to review manually. This applies at the root tools policy, per-agent and per-provider policies, and channel or gateway tool policies. Sandbox lists remain untouched because allow and alsoAllow inherit independently; conflicting sandbox lists still require manual repair. Plugin-owned plugins.entries.*.config is left to the owning plugin’s doctor contract. Gateway startup uses the same permission-preserving repair; unresolved conflicts still require operator guidance before the config can validate.doctor --fix removes workspace: null from agents.entries.<id> so normal workspace resolution can apply. It also removes invalid heartbeat.activeHours windows from agent entries and agents.defaults, preserving other heartbeat settings. Reconfigure a valid window if needed; without an explicit or inherited window, heartbeat hours are unrestricted. These repairs also apply after migrating a legacy agents.list roster.
Ordinary Doctor, including doctor --non-interactive, automatically normalizes a legacy single-file config when the shared migration transforms produce a fully valid result. This also covers older npm updaters that invoke Doctor without --fix. The planner still requires complete plugin validation. Doctor preserves the original in the config backup ring and keeps state migration ordering intact. Includes, externally managed config, newer-written config, and remaining validation errors require the existing explicit repair or operator recovery path. Updaters that explicitly defer plugin repair or advertise a later writable config handoff keep automatic normalization deferred. This does not enable repair maintenance, service changes, or exec-approval migration without --fix.Older Git updaters can keep an in-memory config snapshot and write it after Doctor exits. When that parent marks the update in progress without advertising support for Doctor config writes, Doctor preserves the config and defers importing retired plugin install records, including with --fix. The first fresh Gateway startup then performs the complete migration. Existing canonical plugin install records keep precedence; missing records from the legacy config are imported before that config is rewritten. Startup also handles records restored after the same build previously completed its migration checkpoint.Gateway startup automatically applies deterministic, prompt-free legacy config migrations when an otherwise invalid single-file config can be fully migrated. It uses the same migration transforms as openclaw doctor --fix, validates the complete result including plugin config before writing, and reports the applied changes. The write runs under the startup migration lease and preserves the previous config in the five-slot openclaw.json.bak / .bak.1 through .bak.4 backup ring.Startup checks the authored config revision, included files, and environment-resolved values before migration writes. Runtime path expansion (such as ~/.openclaw/wiki on Windows) does not count as an input change. A real change reports whether the config path, file contents, included files, or resolved values changed; restart so migrations can validate the new inputs.Startup does not migrate configs using $include, configs in Nix mode, or configs last written by a newer OpenClaw version. It also skips automatic config migration while an update is in progress and plugin validation is deferred; the post-update doctor run owns that repair. If any validation or legacy-key issue remains after migration, startup leaves the config unchanged, refuses to start, and prints the openclaw doctor --fix hint. An interactive terminal can still offer to run doctor and retry once for configs that need other repairs; headless services stop with the hint.When model migrations change a configured consumer between subscription/OAuth and metered API-key billing, Doctor reports the consumer, model, and old and new routes after saving the config. The warning also appears in the diagnostic log and update run record. A later Doctor run does not repeat it when the resolved billing route is unchanged. Missing credentials are not treated as proof of a billing change.During an update, Doctor records model-retirement repairs that must wait until plugin installation finishes. The updated OpenClaw completes those repairs after plugin convergence, even when no plugin version changed. openclaw update status records their completion so retired subscription models do not fall through to metered API credentials.Utility-model separation preserves an older config’s implicit primary before recording meta.migrations.utilityModelSeparation: true. Doctor and normal config writes use the previous config to save that primary explicitly; existing primary selections, fallbacks, and credential bindings stay authoritative. This keeps regular chat available when the old implicit primary also served utility tasks. Fresh utility setup records the separation without choosing a primary, and a provider added during utility setup is not mistaken for the previous primary. See agent model configuration.Other commands that encounter legacy keys still ask you to run openclaw doctor. Doctor explains the issues, shows its migrations, and rewrites ~/.openclaw/openclaw.json with the updated schema. Cron job store migrations are also handled by openclaw doctor --fix; automatic config-key migration does not import legacy session stores or repair services.When a readable active config can be fully migrated, Doctor preserves it before considering last-known-good recovery. This includes legacy multi-agent rosters with a default: true owner: unrelated settings and the original agent ownership survive the migration.Per-agent migrations apply to both keyed agents.entries and legacy agents.list rosters, including rosters that already set agents.ownership: "explicit". For example, Doctor preserves an agent’s legacy memorySearch settings under memory.search and converts sandbox.perSession to sandbox.scope. Existing values at the current config paths take precedence.For legacy rosters with multiple agents and no resolvable ambient owner, Doctor seeds agents.defaults.systemAgent.agentId from a uniquely marked default: true agent, or main when present. Sole-agent rosters and legacy default markers already honored by the runtime need no owner repair and produce no missing-owner advice. Explicit fleet ownership disables the legacy default-marker fallback, so those rosters may still need repair. Doctor also pins agents.defaults.heartbeat.agentId only when heartbeat enrollment would otherwise be unresolved; existing heartbeat owners, shared defaults, and per-agent enrollment are preserved. These changes are reported and saved by doctor --fix, including the update-time doctor pass. If no default can be identified, configure the system-agent owner explicitly.
Doctor only carries automatic migrations for roughly two months after a key is retired. Older legacy keys (for example the original routing.queue, routing.bindings, routing.agents/defaultAgentId, routing.transcribeAudio, top-level agent.*, or top-level identity from the pre-multi-agent config shape) no longer have a migration path; config using them now fails validation instead of being rewritten. Fix those keys by hand against the current configuration reference before doctor can proceed.
Active migrations:Doctor names the retired tuning paths it actually removes in one notice, including explicit false values: Removed retired runtime tuning knobs: diagnostics.memoryPressureSnapshot; built-in defaults now apply. Startup repair uses the same migration. Memory-pressure events remain available; use diagnostics export or manual allocation profiling for current evidence.
The Voice Call plugin supplies the migration for its legacy config keys. openclaw doctor --fix invokes it and persists the canonical shape in openclaw.json; runtime config parsing accepts only current keys. Existing canonical settings win over legacy values, including streaming provider credentials, models, and timing. Doctor reports retained destinations instead of claiming those legacy values were moved.
Per-agent memorySearch migrations work with both old agents.list rosters and keyed agents.entries. Doctor preserves explicit memory.search settings when merging legacy values, including environment references moved to the new paths. When repairs affect only per-agent settings, single-file agent includes stay in their included file.When model-policy migration accompanies an agent repair in the same included file, Doctor keeps the explicit policy and repaired settings in that file. A policy-only repair can target a deeper defaults include without rewriting its parent files. Existing include ownership, backup, and conflict checks still apply.The retired tools.message.allowCrossContextSend flag migrates at both root and per-agent scopes. Doctor preserves the effective cross-context permissions, including an agent’s false override of a root true flag.Account-default guidance for multi-account channels:
  • If two or more channels.<channel>.accounts entries are configured without channels.<channel>.defaultAccount or accounts.default, doctor warns that fallback routing can pick an unexpected account.
  • If channels.<channel>.defaultAccount is set to an unknown account ID, doctor warns and lists configured account IDs.
In multi-agent configs, doctor --fix preserves the historical account owner from the original legacy roster. Existing routes remain unchanged. Accounts without historical ownership evidence need an explicit binding; Doctor never promotes a narrower conversation route to account-wide ownership.