fix(#77): surface real reason when account queue times out (no more empty errors)
zhangzhang-bit 在 #77 报告升 v2.0.13 后 Cherry Studio 直接不通一直转圈,
日志显示 Stream error after retries: 后面消息体是空的, 30 秒间隔。
根因不是 v2.0.13 引入, 是一直存在的诊断盲区: waitForAccount() 30 秒
内拿不到任何账号、返回 null 时, stream 和 non-stream 两条主路径都
直接 break 而没给 lastErr 赋值。最终 log 行 lastErr?.message 求值
成 undefined, operator 看到空错误, 客户端看到 30 秒静默失败。完全
没法自诊断是 rate limit / 没权限 / 还是上游卡住。
修复:
- stream queue-timeout 分支 (line ~1710): waitForAccountFn 返回 null
时给 lastErr 赋真实 Error, message 根据 isAllTemporarilyUnavailable
/ isAllRateLimited 分类, 告诉用户具体原因。
- non-stream queue-timeout 分支 (line ~1126): 同样的诊断式 lastErr
注入, 用 { status, body } shape 因为 non-stream 路径 lastErr 是
result object。
- 最终 log fallback: log.error('Stream error after retries:',
lastErr?.message || String(lastErr || 'account queue timed out
without an error object'))。即使未来又有人漏赋 lastErr 也不会
再退化成空字符串。
新增 test/stream-pool-exhausted-error.test.js 三条静态结构断言:
1. stream 路径 queue-timeout 分支必须给 lastErr 赋值 + 调用
isAllTemporarilyUnavailable / isAllRateLimited 分类
2. non-stream 路径同样要求
3. 最终 log 行必须有 || fallback
诊断 by /codex-subagent 高 reasoning effort 独立分析 zhangzhang-bit
日志, 准确锁定 (d) 是高概率根因 + sanity audit v2.0.13 后无其他
ReferenceError / missing await / unreachable branch。
注意这只修了"诊断盲区"。zhangzhang-bit 实际"30s 拿不到账号"的
根本原因是他的账号池里没有对 claude-opus-4-6 有 entitlement 的账号,
或者所有账号都被 rate limited — 这是部署配置问题, 不是代码问题。
v2.0.14 让用户能看到原因, 不再卡 30 秒空错误。
Tests: 243/243 pass (v2.0.13 的 240 + 3 条新回归)。 D
dwgx committed
d998451329badb2ddb91eec54f6304ad27269e56
Parent: e3f40ec