笔记 by 车东

OpenClaw 升级后 Telegram 无响应:修复 invalid persisted session row 与 Legacy exec approvals 迁移

· 运维

OpenClaw 2026.8.1 升级后 Gateway 反复重启,openclaw doctorinvalid persisted session row requires repair,Telegram bot 一直没反应。排查到最后发现:Telegram 无响应的真正原因和 doctor --fix 卡住的其实是同一个东西——未迁移的 legacy exec-approvals.json 触发了 ExecApprovalsMigrationRequiredError 门卫,所有 exec-approvals 消费者(含 telegram 通道)拒绝启动。本文记录完整排查与修复过程。

TLDR

  1. session 损坏session_nodes 表里残留了 2 条窗口缺失的 :run: 别名行(entry_json='{}'),openclaw doctor --fix 会先触发自己的校验守卫报 invalid persisted session row,没法自助修复。手动删掉这 2 条即可。
  2. legacy session store:老的 ~/.openclaw/agents/main/sessions/sessions.json 需要迁移,跑 openclaw doctor --session-sqlite import 导入。
  3. Telegram 无响应(真凶)~/.openclaw/exec-approvals.json 没迁移到 SQLite。assertNoPendingLegacyExecApprovals 门卫在启动时检查这个文件,存在就抛 ExecApprovalsMigrationRequiredError,Telegram 通道起不来,日志里只看到 auto-restart attempt N/10 无限循环——底层错误被 LaunchAgent 的 StandardErrorPath=/dev/null 吞掉了。把 legacy 文件导入 state DB 的 exec_approvals_config 表并改名为 .migrated 后,Gateway 健康监控自动拉起 Telegram,恢复响应。

现象

升级到 OpenClaw 2026.8.1 后,openclaw doctor 报错:

invalid persisted session row requires repair for
agent:main:cron:9638dbe1-...:run:d49e1a0d-...;
stop the Gateway and run openclaw doctor --fix

Gateway 日志里 Telegram 通道反复重启,但看不到任何底层原因:

[telegram] [default] starting provider (@chedong1Bot)
auto-restart attempt 1/10 in ...
auto-restart attempt 10/10 in ...
health-monitor restarting (reason: gave-up)

根因 1:session_nodes 残留窗口缺失的别名行

session_nodes 表以 session_key 为主键。每行有一个 current_session_id 指向 session_windows 表里的窗口(session_id + session_key)。一条 entry_json='{}' 的行只有在其窗口 session_id = current_session_idsession_key 一致时才是合法的;否则就是损坏行。

这次损坏的是两条 cron 任务的 :run: 别名行——父行(cron:xxxcurrent: run:xxx)完好,但多出了一条窗口缺失的 {} 行。openclaw doctor --fix 在 2026.8.1 里的修复路径会先经过 scanCanonicalSqliteSessionEntries 校验守卫,自己把自己挡在外面(抛 SessionCanonicalKeyMigrationRequiredError),无法自助修复。

修复 1:手动删除窗口缺失的 {}

停掉 Gateway 后,在 agent 数据库里定位并删除这两条行(只删窗口缺失的别名行,父行保留):

-- 定位:entry_valid=-1 且无匹配窗口的行
SELECT session_key, current_session_id, entry_valid, status
FROM session_nodes
WHERE entry_valid = -1;

-- 复核:该 current_session_id 在 session_windows 里没有匹配记录
SELECT session_id, session_key FROM session_windows
WHERE session_id = 'd49e1a0d-7d75-4465-8858-deb00a90dfae'
  AND session_key = 'agent:main:cron:9638dbe1-...:run:d49e1a0d-...';

DELETE FROM session_nodes
WHERE session_key = 'agent:main:cron:9638dbe1-...:run:d49e1a0d-...';

根因 2:legacy session store 未迁移

老版本(Session Store JSON)还在 ~/.openclaw/agents/main/sessions/sessions.json,需要迁移到 SQLite。2026.8.1 提供了官方命令。

修复 2:openclaw doctor --session-sqlite import

openclaw doctor --session-sqlite import
# 导入 6 条目 / 3920 事件,归档未被引用的 JSONL

根因 3(Telegram 无响应的真凶):legacy exec-approvals 门卫

Session 修完后 Gateway 起来了,openclaw doctor 也 EXIT 0,但 Telegram 还是没反应openclaw channels status 给出了真相:

- Telegram default: enabled, configured,
  error:Legacy exec approvals exist at /Users/chedong/.openclaw/exec-approvals.json.
  Run `openclaw doctor --fix` before using exec approvals.,
  stopped, disconnected

assertNoPendingLegacyExecApprovals 是 exec-approvals 的迁移门卫:只要 legacy ~/.openclaw/exec-approvals.json 还存在,就抛 ExecApprovalsMigrationRequiredError。Telegram 通道启动时也走 exec-approvals,所以被一并挡住。而 openclaw doctor --fix 本身也卡在同一个门卫上(CLI init gate),形成死锁:只有 doctor –fix 能迁移,但 doctor –fix 先被门卫拦住

日志里看不到这个错误,是因为 LaunchAgent 的 plist 把 stderr 丢掉了:

<key>StandardErrorPath</key><string>/dev/null</string>

修复 3:把 legacy exec-approvals 导入 SQLite

迁移的实质是两条:

  1. 把 legacy 文件内容写进 state DB 的 exec_approvals_config 表(config_key='current'raw_jsonJSON.stringify(file, null, 2) + "\n",以及 socket_path/has_socket_token/agent_count/allowlist_count 等投影列);
  2. 把 legacy 文件改名为 exec-approvals.json.migrated,让 pathMayExist 探针放行。

openclaw doctor --fix 做不了这个,所以写了个幂等的迁移脚本(关键表结构):

CREATE TABLE IF NOT EXISTS "exec_approvals_config" (
  config_key TEXT NOT NULL PRIMARY KEY,
  raw_json TEXT NOT NULL,
  socket_path TEXT,
  has_socket_token INTEGER NOT NULL,
  default_security TEXT,
  default_ask TEXT,
  default_ask_fallback TEXT,
  auto_allow_skills INTEGER,
  agent_count INTEGER NOT NULL,
  allowlist_count INTEGER NOT NULL,
  updated_at_ms INTEGER NOT NULL
) STRICT;

迁移后验证:

INSERTED exec_approvals_config: agent_count=1, allowlist_count=3
DB VERIFY: current|1|3|1|1237

channels status 立即可见 Telegram 恢复:

- Telegram default: enabled, configured, running, connected,
  transport: just now, mode:polling

Gateway 不需要手动重启——健康监控发现门卫释放后会自动拉起通道。

验证

  • openclaw channels status:Feishu / Telegram 均 running, connected
  • openclaw doctor:EXIT 0
  • 之前被门卫挡掉的 WS 命令(agents.listcommands.list)恢复
  • Telegram bot 正常收发消息

命令速查

# 通道健康
openclaw channels status

# 定位 session_nodes 损坏行
sqlite3 ~/.openclaw/agents/main/agent/openclaw-agent.sqlite \
  "SELECT session_key, current_session_id, entry_valid, status \
   FROM session_nodes WHERE entry_valid = -1;"

# 复核窗口是否缺失
sqlite3 ~/.openclaw/agents/main/agent/openclaw-agent.sqlite \
  "SELECT session_id, session_key FROM session_windows \
   WHERE session_id = '<current_session_id>' \
     AND session_key = '<session_key>';"

# 迁移 legacy session store
openclaw doctor --session-sqlite import

# 迁移 legacy exec-approvals(gateway 状态里的报错提示)
# 1. 写 exec_approvals_config 行(config_key='current')
# 2. 把 exec-approvals.json 改名 exec-approvals.json.migrated

# Gateway LaunchAgent 的 stderr 被丢到 /dev/null,
# 排查 provider 启动失败时先改 plist 把错误落盘

注意:涉及 session / exec-approvals 的修复都在改状态库,操作前先备份 ~/.openclaw/agents/main/agent/openclaw-agent.sqlite~/.openclaw/state/openclaw.sqlite