Skip to content
Guides

OpenClaw restart recovery

What actually survives a gateway restart, how interrupted agent turns are detected and resumed, the unclean-boot window that suspends channel auto-start, and the manual overrides that cut through it

7 min read

A gateway restart is the moment when an assistant either quietly picks up where it left off or silently drops a piece of work. OpenClaw treats that as a first class concern: conversations, transcripts, scheduled jobs, background task records and queued outbound messages live on disk rather than in process memory. After a restart, eligible work interrupted mid turn is detected and resumed automatically, without an operator pressing anything. The interesting questions are therefore about the edges, such as what is deliberately not resumed and what happens when resumption keeps failing.

What survives a restart

  • Conversation history sits in a per-agent SQLite database and is untouched by a restart, so sessions continue from the stored transcript rather than starting over.
  • Interrupted main-session turns, subagent runs and background tasks are all reconciled from SQLite after boot, with the subagent registry restored, orphaned background runs recovered or marked lost, and queued outbound deliveries drained and retried.
  • Scheduled cron jobs persist in a SQLite cron store and the scheduler re-arms itself on boot, while a restart sentinel dispatches a one-shot follow-up to whichever session asked for the restart.
  • Gateway terminal sessions are the clear exception, because they live in process memory, end with the old process, and are not recovered for operators or agents.
Recovery is always on and normally needs no manual intervention.

Draining before shutdown

A requested restart does not kill in-flight work immediately. The gateway stops accepting new work, then waits for active agent turns and background tasks to finish, up to a drain budget that defaults to five minutes, so most restarts interrupt nothing at all. Replies to pending node commands are still accepted during the drain, including worker cleanup started by shutdown, though each reply must match its live invocation, node connection, pairing generation and owning lifecycle. Only work that cannot finish inside that budget, or a run cut short by a forced restart or a crash, is aborted, and each affected session is marked for recovery before that happens. On Linux the systemd unit has to use the mixed kill mode so the initial stop signal reaches only the gateway, because older control group units signal child runtimes immediately and can interrupt a turn before the drain finishes. Messages that arrive during the drain window are rejected with an explicit restart error rather than being queued into a dying process. Startup migration warnings do not prevent the gateway from starting: it logs them once and starts degraded, and the status and doctor commands show the running gateway warning report. Errors that leave required state unsafe to read still stop startup outright.

How interruptions are detected

  • At turn admission the gateway appends the user message, marks the session running and records its recovery delivery claim in one SQLite transaction, before any model call or reply hook runs.
  • At shutdown every session with an active run is stamped with a recovery marker in the session store before the run is aborted, which covers the ordinary drain path.
  • At startup the gateway scans session stores for sessions that still claim to be running but have no live owner in the new process, catching hard crashes and kills where no shutdown code ran, and stale transcript lock files are cleaned up at the same time.

A few seconds after startup the gateway re-dispatches each marked session with a synthetic system message saying the previous turn was interrupted by a restart. If a final reply had already been produced but not delivered, its text is included so the agent can send it instead of redoing the work. See how OpenClaw recovers interrupted sessions for the wider picture, and the OpenClaw doctor checks for the repair path when a stale aborted flag conflicts with a tombstone.

Why the budget is bounded

The design choice that matters most here is the refusal to retry forever. Each interrupted main-session cycle carries a durable budget of three charged automatic dispatch attempts, retained across gateway restarts, and an attempt is charged before dispatch, refunded when the gateway explicitly rejects the request before acceptance, and kept when a post-dispatch result is uncertain. Once that budget is exhausted the session is tombstoned and you start a replacement, rather than watching a loop run forever. The same instinct shows up in the crash-loop breaker, where three unclean boots inside five minutes suppress auto-started side services on the next boot while the control plane still starts. It also shows up in the tool restriction chosen before a resume, since states with ambiguous side effects normally continue with restart-safe tools so the model can inspect an outcome without replaying an external effect. Reading the recovery page next to the gateway configuration reference makes that trade-off clearer, and the guarded typing heartbeat hook explains why a resumed turn can still show a typing indicator on the channel it came from.

On Diali

On Diali each customer runs their own assistant, and its state lives on a persistent volume, so the recovery behaviour described here applies to a real restart rather than a clean slate; daily snapshots and one-click restore are available through the Backups add-on (included on Max). The runtime configuration is generated from the dashboard and replaced at each release. See Hosted OpenClaw on Diali for what we host, and Diali security for how we treat that state.

  • Conversations, tasks and queues live on disk, so most restarts lose nothing.
  • A graceful restart drains first, up to five minutes, before anything is aborted.
  • Three failed automatic attempts tombstone a session instead of looping forever.
Get started

Stop reading about it, build one

Set up an agent, pick a channel, and have it working inside the app you already keep open.