#4223

fix(weixin): reload session state after pause expiry

OpenDreamShepherd2006wants to merge intofrom

问题

_poll_once() 检测到 session 过期(errcode -14)后会设置 60 分钟暂停并 sleep。但醒来后直接 return,没有调用 _load_state() 重新读取 account.json。后续轮询继续使用内存中的过期 token,反复命中 errcode -14,形成永久静默的死循环。

复现

  1. nanobot channels login weixin 扫码登录
  2. 等待 session token 自然过期(约 2 小时)
  3. 微信频道永久静默 — 不重新扫码就无法恢复

修复

在 pause sleep 之后调用 self._load_state(),从 account.json 重新加载 token。这样通过 channel rebinding 等方式刷新的 token 能被立即拾取。

变更

nanobot/channels/weixin.py:+1 行

  if remaining > 0:
      await asyncio.sleep(remaining)
+     self._load_state()
      return

影响范围:所有长期运行的 weixin 频道实例。

Activity

5
chengyongru4mo ago

很奇怪哦,我的微信channel一直在跑, 貌似并没有永久静默

chengyongru4mo ago

很奇怪哦,我的微信channel一直在跑, 貌似并没有永久静默

DreamShepherd20064mo ago

很奇怪哦,我的微信channel一直在跑, 貌似并没有永久静默

@chengyongru 感谢 review!

不是"立即静默",是 60 分钟周期的死循环,所以从外部看 bot 仍然"在线",只是不再收发消息:

复现路径

  1. session token 过期(有效期由微信服务器决定,与具体 token 类型有关)
  2. _sync_check() 返回 errcode -14
  3. 设置 _session_pause_until = now + 3600,sleep 60 分钟
  4. sleep 结束后直接 return,不重新加载 account.json
  5. 下次 _poll_once 用内存中仍过期的 token 再次命中 -14 → 回到第 3 步

为什么你没遇到

  • 定期重启 bot(commit 新代码、重启容器等)会使 pause 计数器和 token 同时重置
  • 开发环境频繁开关通道,token 来不及自然过期
  • 生产环境长时间运行的实例才触发

发现过程

我们在实现微信 channel 的 web 端自助绑定(/bind/wechat)时碰到:

  1. 用户重新扫码绑定微信 → account.json 写入新 token ✅
  2. 但 bot 仍不接收消息 ← 检查日志
  3. 发现 errcode -14 → pause 60min → sleep 完直接 return → 再次 -14 → 循环
  4. 重启进程立即恢复(因为启动时调用了 _load_state())

根因:_poll_once 用的是内存里缓存的过期 token,不会重读 account.json 里刷新后的新 token。

修复价值

不光是解决死循环——更重要的是让 web bind 刷新的 token 能被运行时立即拾取。没有 _load_state(),即使通过 /bind/wechat 重新扫码写入了新的 account.json,_poll_once 依然用内存里的旧 token。

验证方式

不需要等 token 自然过期,直接模拟即可:

  1. 手动删掉 account.json 里的 context_tokens / typing_tickets
  2. 下一次 _sync_check 会触发 -14 → 进入 60 分钟 pause
  3. 60 分钟后醒来,无 _load_state() → 兜回 -14 → 再睡 60 分钟
  4. 修了之后:醒来→加载 account.json→拾取新 token→恢复正常
DreamShepherd20064mo ago

很奇怪哦,我的微信channel一直在跑, 貌似并没有永久静默

@chengyongru 感谢 review!

不是"立即静默",是 60 分钟周期的死循环,所以从外部看 bot 仍然"在线",只是不再收发消息:

复现路径

  1. session token 过期(有效期由微信服务器决定,与具体 token 类型有关)
  2. _sync_check() 返回 errcode -14
  3. 设置 _session_pause_until = now + 3600,sleep 60 分钟
  4. sleep 结束后直接 return,不重新加载 account.json
  5. 下次 _poll_once 用内存中仍过期的 token 再次命中 -14 → 回到第 3 步

为什么你没遇到

  • 定期重启 bot(commit 新代码、重启容器等)会使 pause 计数器和 token 同时重置
  • 开发环境频繁开关通道,token 来不及自然过期
  • 生产环境长时间运行的实例才触发

发现过程

我们在实现微信 channel 的 web 端自助绑定(/bind/wechat)时碰到:

  1. 用户重新扫码绑定微信 → account.json 写入新 token ✅
  2. 但 bot 仍不接收消息 ← 检查日志
  3. 发现 errcode -14 → pause 60min → sleep 完直接 return → 再次 -14 → 循环
  4. 重启进程立即恢复(因为启动时调用了 _load_state())

根因:_poll_once 用的是内存里缓存的过期 token,不会重读 account.json 里刷新后的新 token。

修复价值

不光是解决死循环——更重要的是让 web bind 刷新的 token 能被运行时立即拾取。没有 _load_state(),即使通过 /bind/wechat 重新扫码写入了新的 account.json,_poll_once 依然用内存里的旧 token。

验证方式

不需要等 token 自然过期,直接模拟即可:

  1. 手动删掉 account.json 里的 context_tokens / typing_tickets
  2. 下一次 _sync_check 会触发 -14 → 进入 60 分钟 pause
  3. 60 分钟后醒来,无 _load_state() → 兜回 -14 → 再睡 60 分钟
  4. 修了之后:醒来→加载 account.json→拾取新 token→恢复正常