openclaw doctor: Diagnostics and Repair Commands
Reference for the openclaw doctor CLI, covering health checks and guided repairs for gateway, channels, plugins, skills, model routing, local state, and config migrations. Useful for troubleshooting.
Read this when
- You have connectivity/auth issues and want guided fixes
- You updated and want a sanity check
openclaw doctor
Diagnostics and remediation for the gateway, channels, plugins, skills, model routing, local state, and config migrations. Run it whenever behavior seems off and you need a single command to pinpoint the issue.
If Gateway status shows degraded SecretRef owners, doctor issues a Secret runtime degradation warning listing every cold or stale owner, the affected config path, a redacted reason, and the openclaw secrets reload retry command.
When dead-lettering occurs on channel ingress events, doctor identifies each impacted channel account and directs you to openclaw channels dead-letters list for review and recovery.
When exporter health facts are available on the Gateway, doctor presents the latest trusted per-signal state and transport under Telemetry exporters. The summary is redacted, omitting endpoint values, headers, certificates, payloads, and raw errors.
Related:
- Troubleshooting: Troubleshooting
- Security audit: Security
Postures
Doctor operates in five modes:
| Posture | Command | Behavior |
|---|---|---|
| Inspect | openclaw doctor / openclaw doctor --json | Advisory checks in human or machine-readable form. |
| Repair | openclaw doctor --fix | Applies supported repairs, using prompts unless non-interactive repair is safe. |
| Lint | openclaw doctor --lint [--json] | Read-only findings with threshold-based exit codes for CI gates. |
| Shared SQLite maintenance | openclaw doctor --state-sqlite compact | Explicitly checkpoints, compacts, and verifies the canonical shared state DB. |
| Session SQLite tools | openclaw doctor --session-sqlite <mode> | Inspects or maintains SQLite sessions and explicitly imports legacy history. |
For an operator or script wanting the advisory Doctor report in JSON format, openclaw doctor --json is the choice. It exits successfully once the report is generated; health state can be checked via ok and findings. When CI needs a nonzero exit for findings at a chosen severity threshold, use explicit openclaw doctor --lint --json. If a human operator wants Doctor to modify config or state, --fix is preferred.
Following an exec-approval format upgrade, Doctor flags older generated approvals that are inactive because they were never bound to a working directory. openclaw doctor --fix clears those inactive generated entries while preserving manual allowlist rules. Re-run the affected workflows and select Always allow here to re-establish trust for the intended directory. The standard openclaw update finalization applies this safe repair automatically.
Examples
openclaw doctor
openclaw doctor --lint
openclaw doctor --json
openclaw doctor --lint --json
openclaw doctor --lint --severity-min warning
openclaw doctor --lint --all
openclaw doctor --lint --allow-exec
openclaw doctor --deep
openclaw doctor --fix
openclaw doctor --fix --non-interactive
openclaw doctor --generate-gateway-token
openclaw doctor --post-upgrade
openclaw doctor --post-upgrade --json
openclaw doctor --state-sqlite compact
openclaw doctor --state-sqlite compact --json
openclaw doctor --session-sqlite inspect --session-sqlite-all-agents
openclaw doctor --session-sqlite dry-run --session-sqlite-agent main --json
openclaw doctor --session-sqlite import --session-sqlite-all-agents
openclaw doctor --session-sqlite validate --session-sqlite-all-agents --json
openclaw doctor --session-sqlite compact --session-sqlite-all-agents
openclaw doctor --session-sqlite recover --github-issue
openclaw doctor --session-sqlite restore --session-sqlite-all-agents
For channel-specific permissions, use the channel probes instead of doctor:
openclaw channels capabilities --channel discord --target channel:<channel-id>
openclaw channels status --probe
channels capabilities reports the bot's effective permissions for a specific channel target. channels status --probe audits all configured channels and voice auto-join targets.
Options
| Option | Effect |
|---|---|
--no-workspace-suggestions | Turn off workspace memory and search suggestions. |
--yes | Proceed with defaults, no user input required. |
--repair / --fix | Implement suggested non-service fixes automatically (--fix works the same way). Gateway service setup or rewrites still demand manual confirmation or explicit gateway commands. |
--force | Apply thorough repairs, even if it means replacing custom service configurations. |
--non-interactive | Operate without prompts, limited to safe migrations and non-service fixes. |
--generate-gateway-token | Create and set up a gateway token. |
--allow-exec | Permit doctor to run configured exec SecretRefs during secret verification. |
--deep | Check system services for additional gateway installations and report recent Gateway supervisor restart handoffs. |
--lint | Execute updated health checks in a read-only manner and output diagnostic details. |
--post-upgrade | Perform post-upgrade plugin compatibility tests; results appear on stdout; return exit code 1 when any error-level finding occurs. |
--state-sqlite <mode> | Run explicit maintenance on shared state SQLite. The sole mode available is compact. |
--session-sqlite <mode> | Run targeted session SQLite maintenance or legacy import using one of: inspect, dry-run, import, validate, compact, recover, or restore. |
--session-sqlite-store <path> | When paired with --session-sqlite: pick a SQLite database or a legacy sessions.json source, following the mode's selection guidelines below. |
--session-sqlite-agent <id> | When paired with --session-sqlite: choose one configured agent. |
--session-sqlite-all-agents | When paired with --session-sqlite: choose configured and discovered agent stores. |
--github-issue | When paired with --session-sqlite recover: assemble a sanitized openclaw/openclaw issue report; doctor generates it via gh after --yes or manual confirmation. |
--json | Output read-only JSON. Plain --json is informational; add --lint to get threshold-based exit codes. With another machine mode, output that mode's existing JSON report. |
--severity-min <level> | When paired with --lint: remove findings below info, warning, or error. |
--all | When paired with --lint: execute every registered check, including opt-in checks not part of the default set. |
--skip <id> | When paired with --lint: omit a check by its id. Can be repeated. |
--only <id> | When paired with --lint: execute only the specified check id(s). Can be repeated. |
--severity-min, --all, --only, and --skip are valid only when paired with --lint. When used on its own, --json applies the standard read-only lint check set while preserving Doctor's advisory exit behavior. Both read-only configurations refuse --repair, --fix, --force, --yes, and --generate-gateway-token. An explicit --lint additionally rejects --session-sqlite modes along with their selectors, which includes --github-issue. Other machine modes remain free to employ --json for their own output.
Lint mode
Standalone openclaw doctor --json operates read-only and without interaction: no prompts, no repairs, and no config or state changes. It produces the same default findings as lint mode, yet returns 0 after generating a report, ensuring that output formatting does not alter Doctor's usual advisory success contract. Inspect the ok and findings fields of the payload to assess health.
The explicit openclaw doctor --lint posture serves as the deployment preflight. Add --json to obtain machine-readable output while keeping lint's threshold-based exit code unchanged.
openclaw doctor --json
openclaw doctor --lint
openclaw doctor --lint --severity-min warning
openclaw doctor --lint --json
openclaw doctor --lint --all
openclaw doctor --lint --allow-exec
openclaw doctor --lint --only core/doctor/gateway-config --json
openclaw doctor --lint --only core/doctor/local-audio-acceleration --severity-min info
openclaw doctor --lint --only memory-core/managed-local-embedding-setup --severity-min error --json
The managed local embedding setup check acts as a scoped, non-mutating gate before cutover for existing semantic indexes. It activates only through --only or --all, leaving plain doctor --lint behavior untouched. It flags missing llama.cpp setup and the interactive models auth login remediation without asserting full Gateway readiness, launching services, fetching models, or modifying config.
Human-readable output stays concise:
doctor --lint: ran 6 check(s), 1 finding(s)
[warning] core/doctor/gateway-config gateway.mode - gateway.mode is unset; gateway start will be blocked.
fix: Run `openclaw configure` and set Gateway mode (local/remote), or `openclaw config set gateway.mode local`.
JSON output serves as the scripting interface:
{
"ok": false,
"checksRun": 5,
"checksSkipped": 0,
"findings": [
{
"checkId": "core/doctor/gateway-config",
"severity": "warning",
"message": "gateway.mode is unset; gateway start will be blocked.",
"path": "gateway.mode",
"fixHint": "Run `openclaw configure` and set Gateway mode (local/remote), or `openclaw config set gateway.mode local`."
}
]
}
Explicit lint exit codes:
| Code | Meaning |
|---|---|
0 | No findings at or above the selected severity threshold. |
1 | At least one finding meets the selected threshold. |
2 | Command/runtime failure before lint findings can be produced. |
--severity-min governs both which findings appear and the exit threshold: openclaw doctor --lint --severity-min error may print nothing and exit 0 even when lower-severity info/warning findings are present.
Bare openclaw doctor --json returns 0 as soon as it emits a findings payload, including when ok equals false. Argument errors or runtime failures occurring before a payload can be generated still result in nonzero status.
--all determines which checks are chosen prior to severity filtering. The default lint run omits checks that are deep, historical, or prone to surfacing repairable legacy residue; use --all for the full list. --only <id> offers the most exact selector, allowing any registered check to run by id.
core/doctor/local-audio-acceleration reports the auto-selected local STT command, separate capable/requested/observed backend evidence, and fallback order without loading a speech model. It produces an informational finding, so add --severity-min info to see it.
Structured health checks
Modern doctor checks follow a minimal split contract:
detect(ctx, scope?) -> HealthFinding[]
repair?(ctx, findings) -> HealthRepairResult
detect() drives doctor --lint. repair() is optional and executes only under doctor --fix / doctor --repair. Checks not yet migrated to this structure continue using the legacy doctor contribution flow.
Repair contexts may carry dryRun/diff requests; repair results can return structured diffs (config/file edits) and effects (service, process, package, state, or other side effects), enabling converted checks to progress toward doctor --fix --dry-run without shifting mutation planning into detect().
repair() outputs status: "repaired" | "skipped" | "failed" (when no status is given, repaired is implied). If repair yields skipped or failed, doctor explains why and bypasses validation for that particular check. Following a successful fix, doctor reruns detect() limited to the items it just repaired; should the issue persist, a repair warning is issued rather than treating the fix as done.
Each finding carries these fields:
| Field | Purpose |
|---|---|
checkId | Stable id used by skip/only filters and CI allowlists. |
severity | One of info, warning, or error. |
message | Problem description written for humans. |
path | Config, file, or logical path when one exists. |
line / column | Source location when one exists. |
ocPath | Exact oc:// address when a check can target one. |
fixHint | Recommended operator action or repair summary. |
Updated core doctor checks remain tied to the ordered doctor contribution that controls their human-facing doctor / doctor --fix behavior. The shared structured health registry acts as the extension point: bundled and plugin-backed checks execute after core doctor checks once their owning package registers them in the active command path. For plugin authors, openclaw/plugin-sdk/health exposes the same interface.
Check selection
openclaw doctor --lint --only core/doctor/gateway-config --json
openclaw doctor --lint --skip core/doctor/skills-readiness
Both --only and --skip take full check ids and can be specified multiple times. When an --only id is not registered, no check runs for it; to confirm a focused gate picks the intended checks, look for checksRun/checksSkipped in the output.
Post-upgrade mode
openclaw doctor --post-upgrade runs plugin compatibility probes for chaining after a build or upgrade. Findings are written to stdout; exit code becomes 1 if any finding has level: "error". Add --json for a machine-readable envelope ({ probesRun, findings }), which suits CI, the community fork-upgrade skill, and other post-upgrade smoke tooling. If the installed plugin index is absent or corrupt, JSON mode still emits the envelope with a plugin.index_unavailable error finding.
Container image startup breaks the usual "run doctor after updating" rule. When openclaw gateway run launches on a new OpenClaw version, it performs safe state and plugin repairs before signaling readiness. If repair cannot finish safely, startup exits and instructs you to run the same image once with openclaw doctor --fix against the same mounted state/config before restarting the container normally.
Legacy state migration
Persistent file-to-SQLite migrations are owned exclusively by openclaw doctor --fix. It validates and claims each recognized source, writes and verifies canonical rows, records a migration receipt, then removes the retired source. Runtime code never performs lazy imports or fallback reads.
Agent database schema upgrades are reported with the database path and the observed before and after versions, independent of media rewrites. The media persistence message appears only when transcript sessions or trajectory rows were rewritten and includes both counts. A run that does both reports both; an unchanged rerun reports neither.
Device Pair and Active Memory legacy JSON imports check namespace capacity before writing. If the missing entries do not fit, doctor warns and leaves the source unchanged. These imports also verify that source keys and pre-existing destination keys remain in SQLite before reporting completion and archiving the source. A retention warning keeps the source available for inspection and retry; do not delete it to silence the warning, because it may contain state that SQLite did not retain. Resolve the capacity problem before rerunning openclaw doctor --fix.
Microsoft Teams conversation, poll, and SSO token imports also verify that selected legacy keys and pre-existing destination keys remain in SQLite before archiving. Poll imports check both metadata and vote buckets; existing conversation and poll retention rules still select which legacy rows to import. If any required keys are missing, doctor warns and leaves the legacy file in place without reporting completion. Existing SQLite conversations, poll metadata, voter selections, and SSO tokens still take precedence over matching legacy values. These checks do not roll back rows already evicted during import.
Doctor also reports when shared auth still uses the legacy agents/main/agent/openclaw-agent.sqlite owner. openclaw doctor --fix copies its auth profile and runtime-state rows into state/openclaw.sqlite, verifies the exact payloads, removes the source rows, and records the new ownership only after the transaction succeeds. Auth resolution has no dual-read fallback: before migration the legacy database is complete; after migration the shared state database is complete. Once relocated, deleting main no longer risks fleet credentials.
For the retired QMD memory backend, including config rewrites and derived workspace cleanup, see Migrating from QMD.
This includes retired MCP OAuth files under <state-dir>/mcp-oauth/*.json. Stop the Gateway before repair. Doctor imports valid credentials into <state-dir>/state/openclaw.sqlite, preserves an existing canonical SQLite session when both stores exist, drops the obsolete persisted OAuth state value, and uses its receipt to prevent a recreated stale file from resurrecting logged-out credentials. Retired .lock sidecars fail closed: if Doctor reports a stale owner, verify that no older OpenClaw process is running, remove that sidecar, and rerun Doctor.
After explicit repair (--fix, --repair, or --yes), Doctor verifies runtime schema readiness for existing configured, default-layout, and registered databases before reporting completion, including stores whose migration failed before registration. A blocked required migration exits nonzero; stop the Gateway and other OpenClaw processes, then rerun repair. Unrelated advisory warnings, including archived transcript repair failures, do not make a ready database fail this check. Missing databases are not created by the readiness check.
Shared state SQLite compaction
See Database schemas for schema versioning, integrity checks, and downgrade recovery.
openclaw doctor --state-sqlite compact is explicit offline maintenance for
the canonical shared state database at
<state-dir>/state/openclaw.sqlite. It does not accept an arbitrary database
path, is never invoked by normal Gateway operation, and is not part of
openclaw doctor --fix. The command acquires the same state ownership lock as
Gateway startup and holds it through validation, checkpointing, VACUUM, and
the final integrity checks. It refuses to run while a Gateway or another
SQLite maintenance command owns that lock. The state lock remains active when
OPENCLAW_ALLOW_MULTI_GATEWAY=1 skips the per-config Gateway singleton, so an
operator shell does not need to inherit the Gateway service's environment for
maintenance to detect it.
Stop the Gateway and create a verified backup first:
openclaw gateway stop
openclaw backup create --verify
openclaw doctor --state-sqlite compact --json
openclaw gateway start
The command:
- A regular file must exist at the canonical shared-state path. When the
database is absent,
skippedis reported and the exit code is zero. - Before any checkpoint or file modification, the current supported schema
version and
schema_meta.role = "global"are checked. - A non-busy
wal_checkpoint(TRUNCATE)is mandatory. If the checkpoint is busy, shut down any remaining OpenClaw process and try again. auto_vacuumgets set toINCREMENTAL, a completeVACUUMexecutes, and then another checkpoint runs.quick_check,integrity_check, andforeign_key_checkexecute, after which owner-only permissions are reapplied to the database and its SQLite sidecar files.
In JSON output, the database and WAL sizes, freelist pages, page size, and the
auto_vacuum value are reported both before and after compaction, together with
reclaimed bytes and the outcomes of quick_check and integrity_check. foreign_key_check is
enforced with fail-closed semantics and carries no separate success field.
SQLite reports auto_vacuum as 0 when none, 1 when full, and
2 when incremental.
Compaction aborts without mutation if the schema is outdated, newer than the
current OpenClaw build, or tied to an agent database. For an older shared-state
schema, run openclaw doctor --fix first. A newer schema calls for restoring a compatible
backup or upgrading OpenClaw.
Session SQLite migration
Runtime session rows and transcripts reside in SQLite, with ~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite as the
default location. Gateway and local CLI startup never import, restore, or
rewrite legacy session JSON/JSONL files. When a legacy session store is
detected at startup, readiness is refused and a doctor --fix command is printed
for the active profile, rather than serving empty history.
To migrate history from an older file-backed installation, stop the Gateway,
back up its state, and execute openclaw doctor --fix before restarting it. openclaw doctor --session-sqlite <mode> offers
targeted inspection, import, validation, and SQLite maintenance. Legacy
sessions.json files act as migration sources. Hot transcript JSONL files get
imported and archived once import succeeds; archive-tier JSONL files remain
support artifacts and are never used as runtime fallbacks.
During import, transcript payloads are staged in a private, temporary SQLite
database, avoiding retention of full history batches in memory. Free space is
needed on both the system temporary volume and the volume holding OpenClaw
state. Staging is deleted when the operation completes and is never a runtime
store or resumed after interruption; retries rely on the original sources and
committed session data. Individual transcript records are still parsed in
memory. After import, Doctor checkpoints and incrementally vacuums databases
that already support auto-vacuum, with full integrity and foreign-key checks
maintained before and after cleanup. Databases lacking auto-vacuum still
require a full VACUUM to enable it. Incremental cleanup releases unused
pages but does not repack partially filled ones; explicit session and
shared-state compact modes still perform a full VACUUM.
The regular openclaw doctor pass also flags canonical SQLite transcripts whose initial
session header was never persisted. openclaw doctor --fix adds a current header and rebuilds
the transcript indexes in a single transaction, preserving existing event IDs,
parent links, row timestamps, and session-list recency. Headerless legacy or
malformed transcripts stay rejected until their owning migration can validate
them.
Modes:
| Mode | Behavior |
|---|---|
inspect | Read SQLite counts and any selected legacy-source diagnostics without importing; legacy files are not required. |
dry-run | Parse legacy entries and transcript JSONL files, count importable rows, and report issues without writing SQLite rows. |
import | Import legacy entries and transcript events into SQLite for the selected targets. |
validate | Compare the selected legacy sources against SQLite rows and transcript event counts. |
compact | Checkpoint and VACUUM selected agent SQLite databases to reclaim free pages after large deletes or archive cleanup. |
recover | Restore the latest failed migration run, validate its targets, and prepare a sanitized GitHub issue report. |
restore | Restore archived transcript artifacts from recorded migration manifests without deleting SQLite data. |
Selectors:
- Default: the configured default agent store; SQLite inspection does not require a legacy file.
--session-sqlite-agent <id>: one configured agent.--session-sqlite-all-agents: configured agent stores plus discovered agent stores.--session-sqlite-store <path>: one explicit.sqlitedatabase or legacysessions.jsonpath.
dry-run, import, and validate pick existing legacy sources only. In those
modes, an explicit .sqlite path selects no legacy targets and is never parsed
or archived as JSON. For a SQLite target, use inspect, compact, or corruption
recovery with recover. Restoring archived sources from migration manifests
requires the original legacy selector or agent-store discovery that includes
it. Legacy sessions.json selector paths remain supported and resolve to their
corresponding SQLite stores for maintenance.
With the Gateway stopped and its state backed up, inspect and import legacy history:
openclaw doctor --session-sqlite inspect --session-sqlite-all-agents
openclaw doctor --session-sqlite dry-run --session-sqlite-all-agents --json
openclaw doctor --session-sqlite import --session-sqlite-all-agents
openclaw doctor --session-sqlite inspect --session-sqlite-all-agents --json
Before archiving its legacy sources, import validates rows and transcript
event counts. After a successful import, validate may select no legacy targets;
use inspect to view the current SQLite state. While legacy sources remain,
validate returns a non-zero exit when a selected entry is missing from SQLite,
a session id differs, or a transcript event count differs. When running
--session-sqlite-store <path>, verify that the report shows the expected target count; a nonexistent
legacy source selects no targets for dry-run, import, or validate.
SQLite reclaims pages inside the database when rows are deleted, but the file itself may not shrink right away. After removing or archiving sizeable transcripts, execute openclaw doctor --session-sqlite compact --session-sqlite-all-agents to checkpoint WAL files, run VACUUM, and record the database and WAL sizes both before and after. Compaction needs a regular file carrying the current agent schema, its durable database owner metadata, and no open handle held by the doctor process. The destructive import, compact, recover, and restore modes take the same state ownership lock that Gateway startup uses, and they hold it for the entire operation; inspect, dry-run, and validate stay read-only and never acquire it. Shut down the Gateway before proceeding. Destructive modes fail rather than contend with live writes or another maintenance command. A destructive --session-sqlite-store target must reside inside the active state directory; point OPENCLAW_STATE_DIR at the store's owning state directory before maintaining a different installation. Hard-linked targets are rejected because another path could share the same database inode outside the locked state directory. The identical ownership checks apply to SQLite WAL, shared-memory, and rollback-journal sidecars.
A manifest is written under ~/.openclaw/session-sqlite-migration-runs/ by every import before transcript artifacts get moved into the archive. When an explicit import fails after artifacts have moved, leave the Gateway stopped and run recovery:
openclaw doctor --session-sqlite recover --github-issue
Recovery picks the latest failed migration manifest, brings back only the archived artifacts that manifest recorded, checks the affected targets, regenerates the sanitized .failure.md and .failure.json reports, and builds a GitHub issue body that excludes transcript contents, raw environment, secrets, and unbounded config. If no failed migration manifest exists, recovery examines selected SQLite databases through temporary copies of their full file sets. In that disposable copy SQLite can roll back a valid hot journal before quick_check, integrity_check, and foreign_key_check execute, while the original forensic files stay untouched during inspection. After schema and owner validation, recovery tries to repair canonical index corruption in place. Schema, owner, and I/O errors, along with failed or refused index repairs, leave the original database intact and produce a diagnostic. Other confirmed corruption or orphaned sidecars preserve the DB, WAL, SHM, and rollback-journal files by renaming the entire discovered set with a single .corrupt-<timestamp> suffix. If the rename fails partway, already-moved files are rolled back before failure is reported, so a recoverable file set never gets silently split. Stop the Gateway before recovery; copying or renaming an actively changing SQLite file set is unsafe and behaves differently across operating systems. With --github-issue --yes, doctor invokes the GitHub CLI to file the issue in openclaw/openclaw; without confirmation it writes the local support report and prints a prefilled issue URL.
restore is the lower-level undo operation. It relies on manifest sourcePath -> archivePath records, moves archived artifacts back only when the original path is missing, reports conflicts when both paths exist, and leaves the SQLite database alone. When several manifests recorded the same original path, restore plans all candidates before moving any of them. Identical archives are safe duplicates, and one nonempty legacy sessions.json may supersede empty copies created by older writers. Distinct nonempty indexes, distinct transcript archives, invalid archives, and archives missing without a recorded prior restore fail closed so restore cannot silently replace or hide recoverable data.
Downgrading After Session SQLite Migration
Restore the archived legacy transcript artifacts before starting an older file-backed OpenClaw version:
openclaw doctor --session-sqlite restore --session-sqlite-all-agents
Older versions read sessions.json entries and the sessionFile paths recorded in those entries. After the SQLite migration, successful imports move hot JSONL transcripts into session-sqlite-import-archive/, so the older runtime cannot see that history until restore moves those manifest-recorded artifacts back to their original paths.
Restore leaves SQLite data untouched. Sessions created after the SQLite flip exist only in SQLite and will not show up in the older runtime. If you later upgrade again, run the normal migration validation sequence above so OpenClaw can compare restored legacy artifacts with the SQLite rows before importing.
Notes
- In Nix mode (
OPENCLAW_NIX_MODE=1), read-only doctor checks continue to function, butdoctor --fix,doctor --repair,doctor --yes, anddoctor --generate-gateway-tokenare turned off becauseopenclaw.jsoncannot be modified. To change this install, edit the Nix source; for nix-openclaw, follow the agent-first Quick Start. - Interactive prompts, such as keychain or OAuth fixes, appear only when stdin is a TTY and
--non-interactiveis absent. Headless scenarios like cron, Telegram, or no terminal skip these prompts. - Non-interactive
doctorexecutions avoid eager plugin loading to keep headless health checks quick. Interactive sessions still load the plugin surfaces required by the legacy health and repair workflow. --lintis more restrictive than--non-interactive: it is always read-only, never prompts, and never applies safe migrations. Choosedoctor --fixordoctor --repairwhen you want doctor to make modifications.- By default, doctor does not run
execSecretRefs while checking secrets. Only use--allow-exec, with or without--lint, when you deliberately want doctor to execute those configured secret resolvers. - Any config write, including a
--fixrepair, rotates a backup to~/.openclaw/openclaw.json.bak, using a numbered.bak.1...bak.4ring.--fixalso removes unknown config keys flagged by schema validation, listing each deletion; it skips this during an active update so partially written upgrade state is not stripped before its migration completes. - If
openclaw.jsoncannot be parsed and no last-known-good config is recoverable,doctor --fixleaves the file untouched and exits with an error rather than writing a partial replacement. The error points toopenclaw config validatefor the exact parse position and describes how to edit or regenerate the config. - Set
OPENCLAW_SERVICE_REPAIR_POLICY=externalwhen another supervisor manages the gateway lifecycle. Doctor still reports gateway and service health and applies non-service repairs, but skips service install, start, restart, bootstrap, and legacy service cleanup. - Doctor reports the managed Gateway's applied heap limit and the adaptive derivation used for the current host or container memory limit. Outside a repair pass, use
openclaw gateway statusfor the same report. - On Linux, doctor ignores inactive extra gateway-like systemd units and does not rewrite command or entrypoint metadata for a running systemd gateway service during repair. Stop the service first, or use
openclaw gateway install --forceto rewrite the managed base unit. If a systemd drop-in overridesExecStart=orWorkingDirectory=, inspect it withsystemctl --user cat <unit>.serviceand update or remove that drop-in yourself; reinstalling the base does not replace it.Environment=drop-ins remain supported. doctor --fix --non-interactivereports missing or stale gateway service definitions but does not install or rewrite them outside update repair mode. Runopenclaw gateway installfor a missing service, oropenclaw gateway install --forceto replace the launcher.- State integrity checks find orphan transcript files in the sessions directory. Archiving them as
.deleted.<timestamp>needs interactive confirmation;--fix,--yes, and headless runs leave them in place. - Doctor scans historical
~/.openclaw/cron/jobs.jsonstores and previously configured legacy store locations for old cron job shapes, imports jobs and quarantine records into SQLite, and archives the migrated JSON files. - Doctor reports cron jobs with an explicit
payload.modeloverride, including provider-namespace counts and mismatches againstagents.defaults.model, so scheduled jobs that do not inherit the default model are visible during auth or billing investigations. - Doctor reports cron jobs still marked in-flight (
state.runningAtMs), which can makeopenclaw cron listshow them asrunning. This check is read-only: if no Gateway is currently executing a marked job, the next cron service startup records the interrupted run and clears the marker. - Doctor reports legacy image-inspection policy entries named
image.openclaw doctor --fixrewrites supported config allow and deny surfaces and persisted automationtoolsAllowentries toview_image; old-only wildcard patterns such asimage*are preserved and gain an explicitview_image, while patterns that already cover both names remain unchanged. Runtime exposes only the canonical name. - On Linux, doctor warns when the user's crontab still runs the unmaintained legacy
~/.openclaw/bin/ensure-whatsapp.sh, which can misreportGateway inactivewhen cron lacks the systemd user-bus environment. - When WhatsApp is enabled, doctor checks for a degraded Gateway event loop with local
openclaw-tuiclients still running.doctor --fixstops only verified local TUI clients so WhatsApp replies are not queued behind stale TUI refresh loops. - When HTTP(S) proxy environment variables are present but
tools.web.fetch.useTrustedEnvProxyis disabled, doctor explains thatweb_fetchstill uses direct routing, runs a short direct TLS connectivity probe, and names the explicit opt-in. It never enables proxy trust automatically. - The doctor converts legacy
codex/*andopenai-codex/*model references into canonicalopenai/*references across primary models, fallbacks, model allowlists, image/video generation models, heartbeat/subagent/compaction overrides, hooks, channel model overrides, cron payloads, and stale session/transcript route pins. When safe,--fixalso merges legacymodels.providers.codexandmodels.providers.openai-codexconfiguration, migrates legacyopenai-codex:*auth profiles andauth.order.openai-codexentries toopenai:*, shifts Codex intent onto provider/model-scopedagentRuntime.id: "codex"entries, removes stale whole-agent/session runtime pins, and keeps repaired OpenAI agent refs on Codex auth routing instead of direct OpenAI API-key auth. - When nonempty
auth.order.<provider>lists reference profiles that have all vanished but compatible stored credentials exist, doctor reports them.doctor --fixdeletes only those stale overrides, restoring automatic per-agent credential selection; explicit empty orders, partially live lists, and orders without a compatible stored credential stay unchanged. If an active SQLite auth store is unreadable or malformed, doctor explains why it skipped this repair. Restart a running Gateway before rechecking auth status if its config reload mode does not apply the write automatically. - Doctor cleans legacy plugin dependency staging state from older OpenClaw versions and relinks the host
openclawpackage for managed npm plugins that declare it as a peer dependency. It also repairs missing downloadable plugins referenced by config (plugins.entries, configured channels, configured provider/search settings, configured agent runtimes). During package updates, doctor skips package-manager plugin repair until the package swap completes; rerunopenclaw doctor --fixafterward if a configured plugin still needs recovery. If a 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.deny/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 invalidconfigpayload. Gateway startup already skips only that bad plugin so other plugins and channels keep running. - Doctor removes the retired
plugins.entries.codex.config.codexDynamicToolsProfile; the Codex app-server always keeps Codex-native workspace tools native. - Doctor auto-migrates legacy flat Talk config (
talk.voiceId,talk.modelId, and friends) intotalk.provider+talk.providers.<provider>. Repeatdoctor --fixruns no longer report/apply Talk normalization when the only difference is object key order. - Doctor includes a memory-search readiness check and can recommend
openclaw configure --section modelwhen embedding credentials are missing. - Doctor warns when no command owner is configured. The command owner is the human operator account allowed to run owner-only commands and approve dangerous actions. DM pairing only lets someone talk to the bot; if you approved a sender before first-owner bootstrap existed, set
commands.ownerAllowFromexplicitly. - Doctor reports an info note when Codex-mode agents are configured and personal Codex CLI assets exist in the operator's Codex home. Local Codex app-server launches use isolated per-agent homes; install the Codex plugin first if needed, then use
openclaw migrate plan codexto inventory assets that should be promoted deliberately. - Doctor warns when skills allowed for the default agent are unavailable in the current runtime environment (missing bins, env vars, config, or OS requirements).
doctor --fixcan disable those unavailable skills withskills.entries.<skill>.enabled=false; install/configure the missing requirement instead if you want to keep the skill active. - If sandbox mode is enabled but Docker is unavailable, doctor reports a high-signal warning with remediation (
install Dockeroropenclaw config set agents.defaults.sandbox.mode off). - Doctor identifies per-agent
agents.entries.<id>.sandboxDocker, browser, and prune overrides ignored under shared scope. It also warns when an agent's explicit primary model omits fallbacks and therefore disables the defaults' fallback chain; both diagnostics use canonical agent paths after legacy roster normalization. - If legacy sandbox registry files or shard directories are present (
~/.openclaw/sandbox/containers.json,~/.openclaw/sandbox/browsers.json,~/.openclaw/sandbox/containers/, or~/.openclaw/sandbox/browsers/), doctor reports them;--fixmigrates valid entries into SQLite and quarantines invalid legacy files. - If
gateway.auth.token/gateway.auth.passwordare SecretRef-managed and unavailable in the current command path, doctor reports a read-only warning and does not write plaintext fallback credentials. For exec-backed SecretRefs, doctor skips execution unless--allow-execis present. - If channel SecretRef inspection fails in a fix path, doctor continues and reports a warning instead of exiting early.
- After state-directory migrations, doctor warns when enabled default Telegram or Discord accounts depend on env fallback and
TELEGRAM_BOT_TOKENorDISCORD_BOT_TOKENis unavailable to the doctor process. - Telegram
allowFromusername auto-resolution (doctor --fix) requires a resolvable Telegram token in the current command path. If token inspection is unavailable, doctor reports a warning and skips auto-resolution for that pass.
macOS: launchctl env overrides
If you previously ran launchctl setenv OPENCLAW_GATEWAY_TOKEN ... (or ...PASSWORD), that value overrides your config file and can cause persistent "unauthorized" errors.
launchctl getenv OPENCLAW_GATEWAY_TOKEN
launchctl getenv OPENCLAW_GATEWAY_PASSWORD
launchctl unsetenv OPENCLAW_GATEWAY_TOKEN
launchctl unsetenv OPENCLAW_GATEWAY_PASSWORD