跳到正文
FunCoding

搜索

搜索文档、文章、Skill 和 MCP

Legacy state migration

How doctor --fix migrates legacy file-backed state into SQLite

openclaw doctor --fix owns the persistent file-to-SQLite migrations. This page describes each migration source and what to do when one stays blocked.

For a named channel account no longer present in account discovery, Doctor moves its legacy credentials/<channel>-<account>-allowFrom.json file to the existing .migrated archive path (numbered if occupied), reports both paths as a warning, and continues later migrations. The original bytes remain available for recovery; they are not assigned to a surviving account. Malformed allowlists for configured accounts and ambiguous account identities still require repair before migration can continue. Update-time Doctor uses the same behavior.

Matrix's one-time inbound dedupe scan applies only when Matrix is configured or legacy Matrix state needs inspection. A fresh installation with neither does not need a Matrix migration or exclusive Gateway maintenance. Completed scans keep a durable receipt so later Doctor runs do not repeat them.

Pre-June iMessage caches, Active Memory session toggles, Nostr bus and profile state, and Microsoft Teams conversations, polls, SSO tokens, and feedback learnings are no longer imported from JSON files. If those sources remain, Doctor preserves them and directs you to upgrade through 2026.9.5 and run its migrations first. Existing SQLite state remains authoritative.

Pre-July Telegram bot-info, sticker, thread-binding, update-offset, message, sent-message, and topic-name JSON sidecars are no longer inspected or archived. Doctor leaves their files untouched, including empty thread-binding files. If you still need their state, restore a complete pre-update backup and run openclaw doctor --fix on OpenClaw 2026.9.5 before updating again. The separate Telegram JSON ingress-spool migration still imports pending updates, processing claims, and failed tombstones with verified backups.

Pre-July Voice Wake settings, update-check state, plugin-binding approvals, current-conversation bindings, ACP replay, and restart-sentinel JSON are also retired. Doctor preserves these sources and interrupted ACP/restart import claims, then directs you to upgrade through 2026.9.7 and run its Doctor first. Update admission checks the original files before activation. Current SQLite state remains supported.

Retired subagents/runs.json files are also ignored and left untouched; transient runs are never restored from them.

The pre-July plugin install index at plugins/installs.json is no longer imported or archived. Doctor preserves it and stops with the same intermediate-upgrade guidance. July-era SQLite plugin install records remain supported.

Sandbox container and browser JSON registries, including their sharded JSON directories, are retired pre-July state. Doctor reports the retained paths and refuses --fix without reading or changing their contents. Upgrade through OpenClaw 2026.9.7 and run openclaw doctor --fix on the original host before retrying. Current SQLite sandbox registries remain supported.

Session records that need the retired room → groupChannel conversion are refused without changing their original bytes. Preserve the state, install OpenClaw 2026.9.5, run openclaw doctor --fix, then upgrade again. A canonical groupChannel with an ignored room field remains unchanged.

The July Doctor importer could still write provider and lastProvider aliases. Doctor retains their repair, backs up existing SQLite rows, and updates canonical delivery metadata and its query projections together. Runtime reads require that repair; canonical delivery fields and unrelated stored values keep their values.

Legacy state migration

Runtime uses ~/.openclaw/openclaw.json unless you select explicit paths. For a default-layout install with only ~/.clawdbot, Doctor stops the managed Gateway and drains database work, then renames that directory to ~/.openclaw. Config preflight renames clawdbot.json inside it to openclaw.json when the canonical filename is absent. These are same-filesystem moves, not copies; .env and other state bytes stay in the directory. A second pass has nothing to relocate.

If both directories exist, Doctor names both and leaves them for manual reconciliation. It never merges them or leaves a legacy alias. Explicit OPENCLAW_HOME, OPENCLAW_STATE_DIR, and OPENCLAW_CONFIG_PATH selectors are unchanged. A retired JSON plugin install index blocks relocation until the intermediate release has migrated it.

The installed updater runs this same Doctor repair. Published 2026.9.7 retains its original rollback paths, so a later failed update can refuse automatic rollback after relocation. It preserves the moved state and retained snapshots; follow its candidate-Doctor recovery guidance before restarting or downgrading.

openclaw doctor --fix owns general persistent file-to-SQLite migrations. It validates and claims each recognized source, writes and verifies canonical rows, records a migration receipt, then removes the retired source. Gateway, node-host, and local CLI startup leave general legacy repair to Doctor. Normal versioned database opening, native initialization, and recovery of valid current config remain available.

The container image entrypoint automatically runs openclaw doctor --fix --non-interactive against the mounted state and config before starting the Gateway. If you override that entrypoint, run Doctor explicitly against the same mounts. Doctor performs the required legacy repairs under exclusive maintenance ownership and preserves verified SQLite copies before schema upgrades, along with its normal config backups and legacy-file archives. Gateway startup then checks runtime readiness. An unsafe required store exits with code 78 and its specific reason. Refused default or system agents never produce a healthy readiness response. Unused legacy stores, including loose agent/settings.json files without an agent owner, remain untouched and deferred. Doctor reports the retained source and continues independent migrations; startup reports remaining readiness advisories. An advisory never hides a separate required-store refusal.

Doctor also converts older session-row pending-delivery, model-fallback, and memory-flush fields to their current structured state. Published v2026.7.2-beta.5 wrote these fields directly into schema-v16 session rows; stable v2026.8.1 upgrades retain those stored values. This durable upgrade path needs a migration even though current writers already emit structured state. Legacy sessions.json imports apply the same conversion before writing SQLite, preserving exact session IDs and archiving the original JSON bytes. This also runs during update-time Doctor, so flat pending-delivery fields no longer block the import with a repeated request to run doctor --fix. Before rewriting an existing database, it saves and reports a verified SQLite backup, including the original row values. Pending reply text, destinations, intent IDs, timestamps, and memory-flush counts retain their previous meaning; existing current values take precedence. Obsolete retry and error details remain in the backup. This repair changes only those state fields. Transcript locators, other raw metadata, split snapshots, and malformed identity fields remain with their existing repair owners; scalar repair never invents or certifies an identity. SQLite changes only the affected top-level JSON fields, preserving unrelated opaque values, including numeric tokens that JavaScript cannot represent exactly. Doctor uses the existing snapshot and bounded rewrite owners. Its captured file identity also reaches the normal database admission owner, which checks it before changing permissions, journal mode, schema, or rows. Doctor supplies these facts through the database owner's internal repair admission; plugin SDK options stay unchanged. A repair cannot adopt a replacement file after its backup or begin a mutation after its owner has retired. Ordinary session reads require this repair instead of converting the old fields in memory. A Doctor preview reports the affected rows without changing them and defers further session inspection until doctor --fix runs. The update-time Doctor pass performs the same repair before the candidate resumes sessions. The migration prelude upgrades database schemas before retrying deferred scalar repairs. Standalone session import keeps its existing doctor --fix prerequisite for older schemas; session recovery restores or repairs its selected database before inspecting and converting scalar state. If the recovered database still needs a schema upgrade, recovery reports the doctor --fix prerequisite instead of claiming success. Repair failures stop Doctor instead of becoming optional health warnings. If a later batch is interrupted, earlier committed batches stay canonical and the original backups remain available; rerunning Doctor repairs the remaining rows.

If Doctor is interrupted during an agent schema or media migration, stop other OpenClaw processes using that state and rerun openclaw doctor --fix. Doctor reclaims a recorded migration owner only when its host, PID, and process start identity prove that process has ended. Uncommitted database changes roll back; the next pass resumes pending work while retaining the pre-migration backups. Older leases without process identity keep their existing expiry before retry.

A step blocked solely by an earlier refusal keeps refusal.code: "blocked-by-prior-refusal" and includes originatingRefusal with the first refusal's stepId, reason code, and human-readable message. Resolve that originating failure before retrying the blocked steps. These fields travel with stepReceipts, including Doctor refusal errors; they are separate from the persisted import receipts in migration_runs and migration_sources. Older execution receipts may omit originatingRefusal. If a blocked owner can independently validate its input without writing, a verified input error keeps its own step-refused receipt and warnings. The earlier failure remains attached as originatingRefusal. Doctor applies this to legacy TUI last-session JSON: malformed input stays explicit even when an earlier maintenance heartbeat exits. Valid or absent input remains blocked by the prior failure. This diagnostic inspection does not authorize later migrations or writes.

doctor --fix includes the failing check, refusal code, and reason in its halt message and health warnings, using the same failure facts as openclaw update repair. Its bounded summary lists observed refusals before derivative blocked steps; the full receipt list retains the complete chain.

Doctor imports recognized legacy workspace setup files during preflight, before other migrations access workspace state. An existing canonical SQLite setup record wins, including milestones that are absent in SQLite. Doctor does not replay stale milestones over it. Before removing a validated setup file or interrupted claim, Doctor preserves its exact bytes beside the original as <source>.migrated.<sha256>.<unique-id>. The SQLite migration receipt records that archive path and one line per differing milestone (legacy=... canonical=...), which Doctor also prints. With no canonical setup record, Doctor imports the legacy milestones normally. A successful repair removes the runtime blocker; the next run has no workspace setup migration to repeat. Invalid files and workspace identity/version conflicts remain blocked for inspection.

Update rehearsals write only inside their copied state directory. Workspace files are not copied by the rehearsal, so absolute paths retained in legacy records remain read-only inventory. After the candidate is installed, the real Doctor runs the normal import and archival against the operator's state.

Skill Workshop proposals were removed. Doctor exports each pending or quarantined proposal draft, with its support files, to <state-dir>/agents/<agentId>/agent/workshop-skills/.archive/.retired-proposals/<proposal-id>/, then drops the proposal tables. Exported drafts are not live skills; to keep one, ask the agent to save it with /learn so it goes through the normal validated, versioned Workshop write.

Doctor keeps the proposal tables and files, with a recoverable warning, when no configured agent owns a proposal, it cannot export a draft, or it finds a half-finished apply it cannot safely undo. That is a deliberate holdback, not a failed migration, and Doctor still exits successfully: follow the warning. For an unowned draft, add its agent back to the config, or save the whole proposal directory (draft and support files) with /learn and delete it; then rerun openclaw doctor --fix to finish the retirement. The pre-apply contents of a half-finished apply are in the skill_workshop_proposal_rollbacks table until then.

Plugin migrations with declared files outside the copied state are deferred as one plugin operation. Doctor leaves their files and pending markers intact and reports the deferral; configuration repair still runs. Reef's legacy directory follows this rule, including its archived files.

Completed agent deletions that intentionally kept their files are held back during update and migration discovery. Doctor records a recoverable warning naming the agent, database path, and openclaw doctor --fix guidance. These stores do not block active agents' migrations or update rehearsals. If the shared auth source is held, its migration records a skip and dependent auth repairs wait; unrelated Doctor repairs continue. Restore an intended agent before migrating its retained store. Pending file deletion keeps the deletion owner's existing safety checks.

When deletion history is missing, Doctor reports the number of unverified stores held back from repair. Ordinary session creation and database leases record unknown deletion history and continue; a missing row or reconstruction receipt does not make an agent deleted or unusable. Runtime does not recreate an empty journal on existing state. A surviving quarantine/integrity database is evidence of prior state even when the shared database and its registry are gone. Reopening shared state without an agent path preserves unknown deletion history; retained external stores still need Doctor reconstruction before maintenance. Verified fresh SQLite setup initializes the journal normally, without a missing-history warning. Legacy JSON session files alone do not require journal reconstruction. Agent-directory discovery selects openclaw-agent.sqlite and the reserved incognito-openclaw-agent.sqlite name from the agent path owner; custom database names need a configured store, registry entry, or recorded deletion path. Incognito runtime state stays in memory. An existing file at its reserved path is a collision that recovery must inventory and protect, not replace or silently activate as an in-memory store. Normal incognito admission still requires moving or renaming that file before retrying. Sibling reindex locks, captures, and SQLite sidecars are not separate agent stores. Canonical database sidecars still preserve evidence of a missing main database. Previously recorded coordination-file holds remain preserved in recovery receipts but do not produce held-agent warnings. The reindex lock file can remain after its SQLite lease is released; its presence does not mean an agent was deleted. Session SQLite import and recovery hold existing agent databases and their sidecars when deletion history is unavailable, preserving legacy sources without importing or archiving them. Recorded deletion and reconstruction holds and retained plugin inputs with import receipts remain protected. Unreadable history does not erase readable deletion identities or recorded holds. openclaw doctor --fix reconstructs a missing journal and quarantines unusable journal records. Before removing an unusable record, it saves its original row, including malformed JSON, and the held-store inventory under <state-dir>/agents/<id>/recovery/deletion-journal-<unique-id>.json. Missing history gets an inventory receipt in the same recovery directory. Doctor records the maintenance holds in the existing migration tables. It leaves valid in-flight deletions with their lifecycle owner. For regular agents, quarantine retains a safe deletion tombstone until explicit restoration; it never resurrects a deleted agent merely by archiving malformed cleanup details. Recovery preserves held stores; it does not migrate or retire them. Runtime admission remains separate from Doctor's repair holds. Review the paths and use the noninteractive openclaw agents add command printed by Doctor to restore the intended agent, or openclaw agents delete to confirm deletion. An unconfigured agent must be restored before deletion. For a custom database filename, restore the original session.store configuration first; agents add refuses to create an empty replacement when it cannot select a held store. Doctor prints the exact restore and delete commands using the regular agent's ID, such as main. The reserved system agents openclaw and crestodian cannot be added or deleted; a deletion record for either is invalid and is quarantined. Doctor preserves their stores and any held internal SQLite coordination artifacts without recommending an impossible agents add command. Do not assign a preserved system-agent database to a different agent ID.

These holds are visible warnings, not update refusals: leaving the stores in place does not put their data at risk. If Doctor cannot verify a custom store's owner, it leaves the journal unavailable and reports the path. Resolve the inventory or ownership problem, then rerun openclaw doctor --fix.

Invalid configuration also leaves the journal unavailable: Doctor cannot record a complete recovery inventory until it can validate configured ownership paths. Repair the configuration, then rerun openclaw doctor --fix to discover and hold external stores before reconstruction.

The intact historical shared schema written by 2026.7.35 predates the deletion journal. Doctor recognizes that schema and initializes the journal during the shared-schema migration, before migrating the agent databases in the same pass. This does not apply to modern databases with a missing journal or to recorded recovery holds. Unverified deletion-history stores remain held while unrelated repairs and updates continue. Separate integrity, schema, and required-state refusals retain their existing data-preservation checks.

Doctor reports interrupted auth-profile archive recovery even when no new migration remains or you decline another migration. If recovery cannot finish, its warning includes the failure cause and leaves the pending source for recovery; do not delete it to silence the warning.

doctor --fix also repairs an inconsistent completed auth migration only when its old receipt has no credential fingerprints, none of the migrated credentials remain in the current canonical store, and the preserved archive still matches the recorded source hash. Doctor reimports through the normal verified migration flow. Completed receipts with fingerprints, surviving migrated credentials, or no archive remain untouched, so removing credentials after a verified migration does not restore them from backup.

Doctor also retires policy-free exec-approvals.json stubs with empty defaults and agents, including stubs without a version and those containing only socket metadata. It archives the exact bytes as exec-approvals.json.migrated.<sha256>.<unique-id>, records retirement, and leaves existing SQLite policy unchanged. When SQLite has no approvals row, Doctor imports any nonblank socket path or token so a running exec host keeps its credentials. Interrupted .doctor-importing stubs use the same repair path. Unknown fields, unsupported versions, and nonempty or malformed policy are not treated as empty stubs.

For malformed legacy exec-approvals.json, Doctor preserves the original bytes and reports the first validation problem, for example agents entry #2.allowlist[1].lastUsedAt: expected a finite number. Agent entries are numbered from 1 in JavaScript Object.keys order; allowlist indices start at 0. This can differ from JSON text order, especially for numeric keys. To locate entry #2 locally, use Object.keys(JSON.parse(raw).agents)[1], where raw is the file contents. Diagnostics omit agent keys and policy values, and migration receipts contain no diagnostic detail. JSON syntax and invalid UTF-8 receive separate reasons.

Repair the preserved file locally, then rerun openclaw doctor --fix with the same OPENCLAW_STATE_DIR setting (leave it unset if it was unset before). Exec approvals remain blocked until migration succeeds. Explicit repair exits nonzero while the legacy file or an interrupted .doctor-importing claim remains, before restarting any Gateway stopped for that repair. Do not delete the file or broaden its policy to bypass validation.

Agent database schema upgrades are reported with the database path and the observed before and after versions, independently 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.

Doctor resolves configured agent databases and custom session stores before its media/schema migration step. That prerequisite runs before auth-profile imports, session repairs, and post-session plugin repairs, including when preflight inspected a custom store that has not yet been registered.

Media repair detection stops at the first event that needs repair. The repair transaction still validates every transcript and trajectory row before committing; invalid JSON later in either store rolls back the media changes. Databases with no media repairs still receive a complete validation scan, including after imports or restores.

If another connection commits before the media repair transaction starts, Doctor refuses that repair with source changed before migration transaction. Stop other OpenClaw processes using that database and rerun openclaw doctor --fix.

Missing file copies of canonical SQLite transcript archives produce recoverable warnings with the total count and at most five example paths per database. Media and historical transcript migrations still complete, retain the canonical SQLite blobs, and leave deleted copies absent. These warnings do not block the remaining migration steps or database readiness.

Canonical archive repairs commit changed blobs in bounded batches before repairing their file copies. Publication metadata and the historical migration cursor commit together after that batch is verified. Enumeration advances through the complete archive key, including empty historical session IDs. A failed batch reports its archive session and generation and stops; rerunning Doctor resumes after the committed cursor. Blobs already committed before a file or cursor failure remain retained and pending publication. SIGINT and SIGTERM cancel further inspection and repair batches through Doctor's existing maintenance owner, which settles open work and attempts to restore the managed Gateway it stopped. Doctor reports restoration failures and the next recovery action. Updates use the same migration and keep their existing backup and rollback ownership.

Doctor shares its initial fleet schema and ownership inspection across the update guard and admission checks. Database readers use a bounded worker pool, including private snapshots for closed WAL databases, so large fleets do not launch a new process for every agent at every check. Repairs still verify the resulting schemas before reporting completion; only successful recovery of a misplaced copy clears that copy's ownership refusal.

Device Pair's legacy JSON import checks namespace capacity before writing. If the missing entries do not fit, doctor warns and leaves the source unchanged. The import also verifies 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 delegated OAuth tokens still migrate from msteams-delegated.json, which supported June releases wrote. Doctor verifies the imported credentials before archiving the source and preserves a differing existing SQLite token.

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.

If the shared target already contains every legacy profile with identical credential content, Doctor preserves the richer target and completes cleanup, including an empty legacy profile set or older row timestamps. Credential comparison ignores JSON object-key order but preserves every field; it does not select credentials by timestamp. Different credentials, source-only profiles, malformed subset payloads, or differing runtime-state rows remain conflicts. Doctor names conflicting profile IDs and whether their credentials differ, are malformed, or are missing from the target. Store metadata and runtime-state conflicts are reported separately; credential values and arbitrary metadata are never printed.

Stop OpenClaw processes and back up both databases named in the warning before reconciling them locally. For each differing profile, choose the credential to retain and make its complete entry agree in both stores; copy source-only profiles into the target without replacing unrelated profiles. Resolve malformed payloads or differing store metadata and runtime state in the named records, then rerun openclaw doctor --fix. Do not delete either database or the migration receipts to silence a conflict. Pending relocation receipts retain the original source digest through interrupted cleanup. After relocation completes, main-agent rows without a pending relocation receipt remain ordinary per-agent overrides.

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.

Doctor also discovers retired setup state and interrupted migration claims in every resolved agent workspace, active sandbox workspace, and explicitly configured agents.defaults.workspace root. That shared root is included even when an explicit multi-agent roster uses only its subdirectories. Doctor imports <workspace>/openclaw-workspace-state.json through the existing migration; it does not assign the root to an agent or move persona and memory files.

Doctor no longer scans, imports, or removes the older <workspace>/.openclaw/workspace-state.json layout. Its files remain untouched. Upgrade through OpenClaw 2026.9.7 and run openclaw doctor --fix there before updating to migrate that layout. See the migration retention policy.

Repair exits nonzero while retained legacy state still blocks agent turns, even if its data already reached SQLite. Gateway startup and live config candidates check readiness only for the workspaces they would use, not an unused default root. An unready live candidate is rejected and the last-good runtime stays active. Stop OpenClaw processes, save the intended workspace path if the live write was rejected before persistence, and keep the retained files in place. Run openclaw doctor --fix before restarting. Readiness checks never import or delete legacy state.

Pending plugin migrations

When Doctor runs inside an update or repair, let that command finish before following recovery advice from an intermediate plugin warning. The updater may complete package convergence and run Doctor again before it exits.

Doctor can finish an installation-only deferral once the installed plugin's metadata confirms it has no state migration or inspection to run, including when its channel is disabled or it no longer has a config entry. This preserves the plugin's activation settings and saved configuration. An old inspection-only obligation also settles when the available package has no plugin migration contract. Doctor warns with the plugin ID and version and records that reason in the receipt; it does not claim a migration ran or change saved data. Explicit state-migration obligations and packages that still require inspection continue to require completion by the plugin.

If the installed plugin still has not reported migration completion, run openclaw doctor --fix. If that cannot complete the migration, report the remaining warning to the plugin maintainer. Repeating a package update alone does not prove that the plugin migrated its retained state. Keep the retained state and config inputs until the migration owner reports completion.