Safe OpenClaw upgrade with instant rollback. Use when user says "upgrade openclaw", "update openclaw", "check for updates", or any request to upgrade/update...
Single atomic command. Auto-rollbacks on ANY failure. Survives the gateway restart it triggers.
# From an agent session — ALWAYS set escape flag (ensures script survives gateway restart)
_UPGRADE_FORCE_ESCAPE=1 bash skills/upgrade/scripts/safe-upgrade.sh
# Force upgrade even if already on latest
_UPGRADE_FORCE_ESCAPE=1 bash skills/upgrade/scripts/safe-upgrade.sh --force
# Safe read-only checks (no escape needed)
bash skills/upgrade/scripts/safe-upgrade.sh --check # Pre-flight only
bash skills/upgrade/scripts/safe-upgrade.sh --rollback # Manual rollback
systemd-run --user --scope so gateway stop can't kill the scriptnpm i -g openclaw@latestIf ANY critical check fails, the script automatically rolls back — restores install, config, crons, and acpx. Trap handler catches unexpected exits during critical phases.
--check first. Review output with the user._UPGRADE_FORCE_ESCAPE=1 bash skills/upgrade/scripts/safe-upgrade.sh
Do NOT pipe the output (no | tee, no 2>&1 | cat). The script writes to /tmp/upgrade-live.log.~/.openclaw/upgrade-result.json for status/tmp/upgrade-live.log for live outputsuccess: report to user, update any version referencesrolled_back: tell user what went wrong (reason in result file)~/.openclaw/upgrade-backups/current/: script was killed — run --rollback~/.openclaw/upgrade-last.log.openclaw.json)jobs.json)~/.acpx/config.json) if presentBackup location: ~/.openclaw/upgrade-backups/current/
~/.openclaw/upgrade-result.json:
{
"status": "success|rolled_back|rollback_failed|no_change|blocked",
"from_version": "2026.3.2",
"to_version": "2026.3.7",
"message": "...",
"timestamp": "...",
"log": "~/.openclaw/upgrade-last.log"
}
OpenClaw runs as a systemd service. When an agent runs this script, the script is a child process inside the service's cgroup. systemctl stop sends SIGKILL to ALL processes in the cgroup — including the upgrade script. SIGKILL cannot be caught (no trap handler fires).
The script detects this and re-execs itself via systemd-run --user --scope into its own transient systemd scope. The parent process exits immediately — no pipes, no tee, no connections back to the gateway cgroup. This is why piping output is forbidden.
gateway update.run directly — always use this script_UPGRADE_FORCE_ESCAPE=1 when running from an agent session--check is safe to run anytime, changes nothinggolden-snapshot.sh or service-quick-check.py exist in your workspace, they're used; otherwise silently skippedZIP package — ready to use