This was the worst OpenClaw update I have had.

Not because one command failed. That would have been easy. It was worse: Telegram disappeared immediately after the update, openclaw doctor could not repair it, the local TUI was broken too, the first workaround degraded the system, and the beta recovery path corrupted enough state that I had to bring in a standalone Claude session from outside OpenClaw to patch SQLite manually.

The package update was only the first problem. The real failure was losing the very control plane I normally use to fix OpenClaw.

Timeline

The short version:

  • The update to 2026.7.1 looked acceptable from the changelog review.
  • Immediately after updating, Telegram access died.
  • I logged into the server directly.
  • openclaw doctor could see some problems but could not get me back to a working assistant.
  • The local OpenClaw TUI was broken as well, because the Codex plugin crashed on openSyncKeyedStore.
  • I disabled Codex so the gateway could start at all.
  • That restored Telegram, but removed the native /codex path I actually rely on.
  • I changed the model route to a backup Claude Opus path and pushed forward to 2026.7.2-beta.2.
  • The beta made the situation worse: the TUI was broken for every model, not just Codex.
  • I ended up running a standalone Claude session outside OpenClaw to repair corrupted SQLite state manually.
  • After the gateway recovered, raspi5 was still behind, pending reapproval, and carrying retired node config.
  • After fixing the Pi, the final Telegram status report failed to deliver.

That is a grim little sequence. It starts as a routine update and ends with external surgery on the assistant’s own state database.

The Update Command Was Supposed To Prevent This

The annoying part is that I was not updating blindly.

I previously wrote about building a safer OpenClaw update workflow in Never Fear the Update Button Again. The point of that command was precisely to avoid “run update and pray.” It checks the current version, checks update status, reviews release notes, looks for breaking changes, updates nodes before the gateway, watches the restart, runs doctor, scans logs, checks cron health, and reports back through Telegram.

For this update, the changelog review did not show anything that looked like “your control plane will collapse.” The update seemed acceptable.

That is the important lesson: changelog-aware automation is necessary, but it is not enough when the break is in the plugin registration path, the local TUI path, and the persisted migration state.

Why Codex Was A Load-Bearing Plugin

Codex was not a decorative integration in this setup.

I moved the interactive OpenClaw path to GPT-5.5 through Codex OAuth for reasons I already wrote about in Why I Moved OpenClaw from Anthropic API to OpenAI GPT-5.5: cost, rate limits, and the need for a strong always-available interactive assistant. I also wrote about the broader routing problem in How I Fixed OpenClaw After Context Bloat, Rate Limits, and Bad Model Routing: interactive work and background work need different model economics.

So when Codex broke, it was not “one plugin is unavailable.” It was the primary interactive path, the native /codex plugin, the app-server harness, the Codex-managed GPT catalog, and Codex-backed text inference/media-understanding/web-search capabilities all becoming suspect at the same time.

The fallback Claude route was useful, but it was a backup path, not the normal operating model.

The First Break: 2026.7.1

The first failure was the update to OpenClaw 2026.7.1.

After the update, Codex crashed during registration because it called openSyncKeyedStore, which OpenClaw core no longer exposed. That broke the local TUI as well as Telegram recovery, because the same plugin path was part of the system’s startup surface.

This is the worst category of assistant failure: the assistant breaks, then the interface you would normally use to ask the assistant to fix itself is also broken.

The emergency recovery was blunt:

  • disable Codex so it could not crash the gateway on register
  • purge the stale Codex install record from installs.json
  • remove OpenClaw’s auto-generated installs.json.migrated
  • wait for the five-minute migration lock to expire
  • start the gateway clean

There was a second bug hidden inside that process. The gateway had a stale Codex install record in ~/.openclaw/plugins/installs.json: it recorded one installed version while the actual package was another. Migration refused to reconcile the conflict.

Then OpenClaw’s own installs.json.migrated archive made it worse. After archiving the legacy file, the next boot was still re-reading that archive and rediscovering the same conflict from the old plugins[] array. The safety copy had become part of the active failure loop.

That is a beautifully stupid failure mode: a migration safety copy that keeps poisoning the migration.

Disabling Codex got Telegram back, but only by amputating the part of OpenClaw I normally use most.

The Beta Was Worse

At that point I had a degraded but reachable system. I changed the model route to a backup Claude Opus path and tried the beta recovery: gateway and Codex package to 2026.7.2-beta.2.

The hope was simple: if 2026.7.1 broke because Codex and core were out of step, maybe the beta had the matching API change.

Instead the beta made the local control plane worse. The TUI was broken for any model, not just Codex. This is where the update moved from annoying to genuinely bad: I could no longer trust the normal OpenClaw surfaces to recover OpenClaw.

The eventual fix came from outside the system. I ran a standalone Claude session, inspected the broken state directly, and manually repaired corrupted SQLite records so the gateway could start cleanly again.

After that, the gateway finally came back on 2026.7.2-beta.2. Telegram was alive. Codex was loaded. The configured default model worked again. Doctor showed warnings rather than a fatal startup problem.

But the update had still left a distributed system in a mixed-version state:

  • Gateway: 2026.7.2-beta.2
  • Codex: 2026.7.2-beta.2
  • Raspberry Pi node: still 2026.7.1

The Pi was technically connected, but it was stale. Its advertised capabilities needed reapproval, and it still had retired node configuration on disk. So from a status-dashboard point of view things looked mostly alive, while from an operational point of view one of the useful remote execution surfaces was not actually ready.

The next leftover failure was restart recovery. Logs showed that an interrupted main session could not be adopted cleanly after restart because pre-hook recovery could not bypass the active before_agent_reply hook. In plain English: the gateway was alive, but some resumed or queued turns got wedged after the restart. That is exactly the sort of state machine bug that only appears when you update a live conversational system mid-flight.

The last failure was the most irritating because it was user-visible: after fixing the Pi, the final Telegram status message got swallowed and a generic “message failed” marker appeared instead. The repair had succeeded. The report about the repair had failed.

Which is, frankly, the most automation thing imaginable.

The Pi Repair

The Pi could not be reached by normal LAN SSH from the VPS. ssh raspi5 and direct access to 192.168.0.23 were dead ends from that side of the network. The working path was the reverse SSH tunnel:

ssh -p 2222 [email protected]

Once on the Pi, the fix was straightforward:

sudo npm install -g [email protected]
sudo systemctl restart openclaw-node
openclaw doctor --fix --yes --non-interactive
sudo systemctl restart openclaw-node

The important bit was the doctor migration. The Pi still had /home/timur/.openclaw/node.json, which had become retired configuration. Until that was removed, the node was carrying old state into a new runtime.

After the restart and reapproval, the gateway saw raspi5 connected and approved on 2026.7.2-beta.2, with the expected commands back: system.run, system.run.prepare, system.which, fs.listDir, Ollama tools, and MCP tools.

The remaining Pi warnings were real but not urgent:

  • gateway.mode unset
  • plaintext gateway.auth.token
  • no Pi-side OpenAI key for memory search

Those are hygiene issues. They should be fixed, but they were not the reason the node was unusable.

What The Update Check Missed

The existing update automation was built around a sensible idea: review the changelog, update nodes first, update the gateway, watch the restart, run doctor, scan logs, check cron health, and report back. That catches obvious breakage.

It did not catch this, because “gateway is alive” is too small a definition of success. OpenClaw is not one process anymore. It is a gateway, Codex sessions, Telegram delivery, background hooks, cron jobs, remote nodes, reverse tunnels, approval state, local node configs, and whatever was in-flight when the process restarted.

The failure was that the system had too many definitions of “healthy”:

  • systemd thought the service was running
  • the gateway thought the package was current
  • plugin migration thought it had archived old state while still re-reading it
  • Telegram could deliver inbound messages
  • the Pi was visible but stale
  • Codex was loaded but restart recovery had wedged an interrupted session
  • the final user-facing repair report failed to send

Each individual layer could truthfully say something was fine. The whole system was not fine.

The next update ritual needs to verify the edges, because that is where the user feels the breakage:

  • can I receive a Telegram message?
  • can I send one?
  • can I open the local TUI?
  • can I start a fresh Codex turn?
  • is Codex actually enabled and registered, not merely installed?
  • are plugin install records and migrated archives consistent?
  • is the SQLite-backed runtime state internally consistent?
  • are interrupted turns recoverable, or at least safely abandoned?
  • are all nodes on the expected version?
  • are all node capabilities approved?
  • can the Pi actually run a command?
  • are migrations complete on both gateway and nodes?

Until those checks pass, the update is not done. It is merely installed.

Today the package update succeeded. The system update did not. That distinction is the whole postmortem.

Follow-up, 13 August 2026: the next incident exposed a different release-process failure: beta core had outrun its lockstep Codex plugin, producing an endless gateway boot loop. Read The OpenClaw Update That Could Not Possibly Succeed.