OpenClaw Database Schemas: Locations, Versions, and Recovery

Learn where OpenClaw stores its global and per-agent SQLite databases, how schema versions and integrity checks work, and how to recover from downgrade issues. Essential for administrators and developers managing OpenClaw data.

Read this when

  • Diagnosing a newer database schema error
  • Checking database compatibility before an update or downgrade
  • Proposing a SQLite or persistent-store change
  • Preparing storage operations for another database backend
  • Recovering a database for an older OpenClaw release

OpenClaw keeps control-plane state in a single global SQLite database, while each agent gets its own SQLite database for its data. When a database is opened, schema migrations run only in the forward direction. A database written by a newer schema is rejected by older OpenClaw builds.

Database layout

ScopeDefault pathContents
Global control plane~/.openclaw/state/openclaw.sqliteShared configuration state, registries, approvals, plugin state, and shared runtime state
Per-agent data plane~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqliteSessions, transcripts, memory indexes, auth state, conversation state, and agent-scoped runtime state

Certain features that are high-volume or tied to specific lifecycles rely on separate SQLite stores, among them the task registry and trajectory data.

Versioning contract

Every database keeps its schema recorded in two locations:

  • PRAGMA user_version holds the SQLite schema version.
  • The main schema_meta row stores role, agent_id, schema_version, and app_version. app_version identifies the OpenClaw build that most recently wrote the schema metadata.

Forward-only migrations are applied by OpenClaw whenever it opens an older supported database. If a database's user_version is newer than the running build, OpenClaw declines to open it and raises a newer schema version error. Before startup, the Gateway verifies every registered database. A package or source target whose declared schema support is older than an on-disk database is likewise refused by openclaw update. Preflight checks cannot be performed on target packages that were published before schema metadata existed.

Staying at the same schema version is permitted only when downgraded readers remain safe. Older builds ignore new tables, so those qualify. An existing table may gain a column only if its declaration is exactly one bare nullable SQLite STRICT datatype, namely ANY, BLOB, INT, INTEGER, REAL, or TEXT, and nothing else. The declaration must not carry a default, NOT NULL, a primary or unique key, a check, a reference, a collation, a generated expression, or any other suffix. When an addition to an existing table is constrained, a schema-version bump or a companion table is required instead.

Matching numeric versions are a necessary condition but not a sufficient one. A release can introduce a lazy or startup-repairable table, column, index, or trigger without incrementing user_version, so two databases sharing a version number can still differ in shape. OpenClaw checks the canonical table definitions, constraints, indexes, triggers, virtual tables, and table options that the running release owns.

Manual installation of OpenClaw via npm skips the updater guard. Even so, database open checks will still reject an incompatible build.

Preflight a target release

Prior to activating or rolling back a release, run that release's CLI against one explicit copied state database:

openclaw database preflight <copied-state.sqlite> --json

The command neither reads the default state directory nor modifies the supplied file. It treats the supplied consolidated file as immutable and read-only, compares the target release's own schema contract, and reports one outcome:

  • exact: the copied database matches the target release's runtime schema. Feature-local tables that are intentionally absent until first use do not require repair.
  • startup-repairable: the numeric version matches and a runtime-owned additive difference remains; startup needs a write to converge the shape.
  • migration-required: the database is older than the target release.
  • incompatible: the database is newer, or its same-version shape has blocking drift such as an unexpected column.
  • indeterminate: the file, integrity metadata, or ownership metadata could not be verified.

JSON output is identified by schema: "openclaw.state-schema-preflight.v1".

Take a SQLite online backup or another WAL-aware snapshot captured while the source is safely coordinated. The preflight input must be a single consolidated file with no sibling -wal, -shm, or -journal; sidecars make the result indeterminate. Do not copy only the main .sqlite file from an active WAL database. Preflight the exact runtime that will be activated; a package version or numeric schema version alone does not prove same-version shape compatibility.

Agent schema history

VersionChangeFirst release
1Initial per-agent store (#88349)v2026.5.30-beta.1, stable through v2026.7.1
2Memory index identity (#104449)v2026.7.2-beta.1
4Sessions and transcripts moved into SQLite (#98236)v2026.7.2-beta.1
5-6Terminal freshness and state lifecycle (#104859)v2026.7.2-beta.1
7Per-entry lifecycle status projection (#106151)v2026.7.2-beta.1
8Per-transcript session provenance (#106766)v2026.7.2-beta.2
9STRICT tables (#108663)v2026.7.2-beta.2
10Materialized active transcript paths (#108851)Unreleased
11Durable delivery, conversation addresses, and heartbeat outcomes (#109636, #95838, #109999)Unreleased
12Session-owned ACP parent-stream eventsUnreleased
13Durable transcript rewrite watermarksUnreleased
14Logical session nodes, generation windows, and node-owned artifact foreign keysUnreleased
15Board and session-sharing tablesUnreleased
16Legacy top-level transcript media fields retiredUnreleased
17Tenant-free per-agent lease table retired after the last writer and routing arm were removed (#121113, #121615)Unreleased

Version 3 was an unshipped development step folded into version 4.

State schema history

VersionChangeFirst release
1Initial shared state databasev2026.5.30-beta.1
2Metadata-only message audit events (#103903)v2026.7.2-beta.1
3STRICT tables and schema-drift hardening (#108663)v2026.7.2-beta.2
4Session watch provenance replaces encoded sentinel rowsUnreleased
5Durable cloud-worker result references on pending workspace fences (7a7d6bb, #110952)v2026.7.2-beta.4
6Every committed shared-state table becomes part of the canonical runtime schema (509a5f0, #113473)v2026.7.2-beta.5
7Retired inferred-commitment storage removedUnreleased

Integrity checks

WhenCheck
Every openValidate the schema_meta table and primary metadata row
Before a pending migrationRun a full integrity, foreign-key, role, schema, and index scan
Gateway background verifierRun the full scan about once daily and log results
Doctor, backup verification, and compactionRun the full scan before accepting or rewriting the database

Schema headers are all the Gateway startup preflight reads. For an explicitly copied file, openclaw database preflight handles the release-local shape comparison. The slower recurring full scan for live databases that don't require migration is owned by the background verifier. Quarantine decisions are stored only in a dedicated openclaw-quarantine.sqlite store, so they persist even if the databases under quarantine are damaged. Verification results get logged.

Troubleshooting

Why you cannot go back after updating to 2026.7.2

Agent schema 1 and state schema 1 were used by every release up to v2026.7.1. On first start, the 2026.7.2 release train (beginning with v2026.7.2-beta.1) migrates your databases forward. This migration is irreversible: the data gets rewritten into the newer schema, and installing an older OpenClaw later won't reverse it. The older build refuses to start with a newer schema version error that names the build owning the database.

Data is never downgraded by downgrading the binary. If you need to run a release older than 2026.7.2 after updating, these three options exist:

  1. Restore a backup taken before the update. Create and verify backups before major updates.
  2. Run the older build against a separate state directory (OPENCLAW_STATE_DIR). It starts fresh; your migrated data stays untouched for when you return to the newer build.
  3. Follow the manual downgrade procedure below. It is unsupported and risks data loss without a verified backup.

Since 2026.7.2, openclaw update refuses to install a release that cannot open your current databases, so the updater will not put you in this situation. Installing an older version manually through npm bypasses that guard; the databases still refuse the old binary, but only after it is installed.

The Gateway refuses to start with a newer schema version error

A newer OpenClaw build wrote your databases, and the running build is older. The error names the refusing install, release version, commit, and install root, plus the schema it supports and the schema it found.

Act on the install root, not the version. One release version string spans many main commits, schema levels, and same-version schema shapes, so two installs can both call themselves 2026.7.2 and still disagree about a database. A prerelease version may not exist on the latest npm tag at all: check npm view openclaw dist-tags before reinstalling, because the tag carrying the schema you need may be beta, and reinstalling from latest can move you further away.

A linked source checkout is the case where the commit misleads: openclaw --version reports the checkout's git HEAD, but the code actually executing is whatever dist/ was last built. If the install root is a checkout, rebuild it (pnpm build) before concluding the version is wrong.

Open the database with a build that supports its schema, or point the older build at a separate OPENCLAW_STATE_DIR. Do not edit the database to silence the error.

A database is quarantined after integrity verification failed

The background verifier proved the file is corrupt, and every open now fails fast instead of rescanning. Restore the database from a backup or repair it, then run openclaw doctor --fix to clear the quarantine record. Doctor reports an explicit error if the quarantine record itself cannot be cleared; rerun it until it reports clean.

Downgrades are unsupported

Manual schema downgrades are for agents and operators who accept the risk. Create and verify a backup before editing any database. Stop the Gateway and every process that can open the database.

The general procedure is:

  1. Read the target release's schema and migrations.
  2. In one transaction, drop every table, index, trigger, and column introduced after the target version.
  3. Set PRAGMA user_version and schema_meta.schema_version to the target version.
  4. Run the target release's full database verification before starting the Gateway.

Example: state schema 7 to 6

Schema 7 removed the retired shared commitments table. A schema 6 build still requires that canonical table, so a manual downgrade must recreate its exact empty schema before lowering the version.

Run equivalent SQL against the global state database after inspecting the exact schema that wrote it:

BEGIN IMMEDIATE;

CREATE TABLE commitments (
  id TEXT NOT NULL PRIMARY KEY,
  agent_id TEXT NOT NULL,
  session_key TEXT NOT NULL,
  channel TEXT NOT NULL,
  account_id TEXT,
  recipient_id TEXT,
  thread_id TEXT,
  sender_id TEXT,
  kind TEXT NOT NULL,
  sensitivity TEXT NOT NULL,
  source TEXT NOT NULL,
  status TEXT NOT NULL,
  reason TEXT NOT NULL,
  suggested_text TEXT NOT NULL,
  dedupe_key TEXT NOT NULL,
  confidence REAL NOT NULL,
  due_earliest_ms INTEGER NOT NULL,
  due_latest_ms INTEGER NOT NULL,
  due_timezone TEXT NOT NULL,
  source_message_id TEXT,
  source_run_id TEXT,
  created_at_ms INTEGER NOT NULL,
  updated_at_ms INTEGER NOT NULL,
  attempts INTEGER NOT NULL,
  last_attempt_at_ms INTEGER,
  sent_at_ms INTEGER,
  dismissed_at_ms INTEGER,
  snoozed_until_ms INTEGER,
  expired_at_ms INTEGER,
  record_json TEXT NOT NULL
) STRICT;

CREATE INDEX idx_commitments_scope_due
  ON commitments(agent_id, session_key, status, due_earliest_ms, due_latest_ms);

CREATE INDEX idx_commitments_status_due
  ON commitments(status, due_earliest_ms, due_latest_ms);

CREATE INDEX idx_commitments_scope_dedupe
  ON commitments(agent_id, session_key, channel, dedupe_key, status);

CREATE INDEX idx_commitments_agent_due
  ON commitments(agent_id, status, due_earliest_ms, due_latest_ms, session_key);

CREATE INDEX idx_commitments_agent_sent
  ON commitments(agent_id, status, sent_at_ms, session_key);

PRAGMA user_version = 6;
UPDATE schema_meta
SET schema_version = 6,
    updated_at = unixepoch('now') * 1000
WHERE meta_key = 'primary';

COMMIT;

The recreated table starts empty because schema 7 discarded the retired rows. A botched downgrade means restore from the verified backup.

Example: agent schema 17 to 16

Schema 17 removed the tenant-free per-agent lease table. A schema 16 build still requires that canonical table, so a manual downgrade must recreate its exact schema before lowering the version.

Run equivalent SQL against each affected per-agent database after inspecting the exact schema that wrote it:

BEGIN IMMEDIATE;

CREATE TABLE state_leases (
  scope TEXT NOT NULL,
  lease_key TEXT NOT NULL,
  owner TEXT NOT NULL,
  expires_at INTEGER,
  heartbeat_at INTEGER,
  payload_json TEXT,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL,
  PRIMARY KEY (scope, lease_key)
) STRICT;

CREATE INDEX idx_agent_state_leases_expiry
  ON state_leases(expires_at, scope, lease_key)
  WHERE expires_at IS NOT NULL;

CREATE INDEX idx_agent_state_leases_owner
  ON state_leases(owner, updated_at DESC);

PRAGMA user_version = 16;
UPDATE schema_meta
SET schema_version = 16,
    updated_at = unixepoch('now') * 1000
WHERE meta_key = 'primary';

COMMIT;

The recreated table starts empty because schema 17 has no agent-DB lease tenants to preserve. A botched downgrade means restore from the verified backup.

2,085 words · updated Aug 17, 2026