系统版本
Debian GNU/Linux 13 (trixie) x86_64,腾讯云轻量应用服务器(ap-guangzhou),出口带宽 3 Mbps
QQNT 版本
3.2.33-52892
LLBot 版本
8.2.1(有头模式 ./llbot --pmhq,PMHQ v8.1.1)
OneBot 客户端
为排除框架干扰,测试直接用 ws 客户端连 LLBot 的正向 WebSocket(127.0.0.1:3001)调用 upload_group_file,未经过任何机器人框架。 原始问题也稳定复现于 TRSS-Yunzai 3.1.3。
发生了什么?
从 7.12.x 升级到 8.x 之后,upload_group_file 上传较大文件(本次测试 50MB)稳定失败;7.12.x 时期同样的文件可以正常上传。
定位到 src/ntqqapi/api/file.ts 的 uploadGroupFile:8.x 删除了 HighwayTcpSession,只保留 HTTP 分块上传。
v7.12.15
try {
await new HighwayTcpSession(trans).upload()
} catch {
await new HighwayHttpSession(trans).upload()
}
v8.2.1
await new HighwayHttpSession(trans).upload()
src/ntqqapi/helper/highway.ts 里 HighwayTcpSession 类也整个不存在了(文件从 192 行增至 472 行,同时新增 concurrency = 3 与每请求 timeout: 11 * 1000)。
为验证这是否就是直接原因,我在 8.2.1 的构建产物里照 v7 的实现补回了一个 HighwayTcpSession,并把 uploadGroupFile 改回「TCP 优先、失败回退 HTTP」,然后做了对照实验。
对照实验结果
条件:同一台机器、同一个群、同一份 OneBot 调用;每一轮都重新生成一个全新的 50MB 随机文件(head -c 52428800 /dev/urandom,md5 各不相同,避免服务端 fileExist 秒传导致上传分支被跳过);两组在同一个半小时内完成。
| 组 |
通道 |
结果 |
耗时 |
失败位置 |
| 打补丁 1 |
TCP |
成功 |
153.3s |
— |
| 打补丁 2 |
TCP |
成功 |
154.8s |
— |
| 打补丁 3 |
TCP |
成功 |
154.3s |
— |
| 原版 1 |
HTTP |
失败 |
125.9s |
offset 2097152(2 MiB)— Highway request timeout (11s) |
| 原版 2 |
HTTP |
失败 |
141.6s |
offset 8388608(8 MiB)— Highway request timeout (11s) |
| 原版 3 |
HTTP |
失败 |
137.7s |
offset 42991616(41 MiB)— HTTP Upload failed with code 102902 |
TCP 3/3 成功且耗时高度一致(153~155 秒,正好是 50MB 跑满 3 Mbps 出口的时间);HTTP 0/3。
两点补充观察
1. 失败位置是随机的,不存在固定阈值
三次分别断在 2 MiB、8 MiB、41 MiB。我最初在另一台机器上连续四次都断在 offset 42991616,一度以为存在「41 MiB 硬上限」,但扩大样本后确认那只是巧合。这与 #829 报告的「50多M-60多M的任意大小处断开」一致。
2. OneBot 层返回的 retcode=1200 请重试 会掩盖真实原因
三次失败在 OneBot 层都是 {"status":"failed","retcode":1200,"wording":"请重试"}。由于测试文件是纯随机二进制(不含任何可被内容审核的信息)却同样触发,可以确认这个提示与内容审核无关,真正原因只体现在 LLBot 日志的 Highway 明细里。建议把 Highway 的错误信息透传到 OneBot 响应中,否则使用者很容易误判为账号风控或内容问题。
#829 报告的也是 102902,被标记为「v8.1.4 已修复」并关闭。但查修复提交 def580ed 的 diff,实质改动只有两行:
- readonly concurrency = 4
+ readonly concurrency = 3
- timeout: 10 * 1000,
+ timeout: 11 * 1000,
其余为代码格式调整。这降低了触发概率但未消除问题,在 8.2.1 上仍然稳定复现(本次 0/3)。
如何复现
- 准备一个 50MB 的随机文件(务必每次重新生成,否则服务端
fileExist 会秒传,根本不会走 Highway):
head -c 52428800 /dev/urandom > /tmp/test-50MB.bin
- 通过 OneBot 调用上传到群:
{ "action": "upload_group_file",
"params": { "group_id": 123456789, "file": "/tmp/test-50MB.bin", "name": "test-50MB.bin" } }
- 重复 3 次,每次换一个全新随机文件。
在 3 Mbps 出口的机器上 3/3 失败。出口带宽越低、单次上传耗时越长,越容易触发。
期望的结果?
upload_group_file 能稳定上传大文件,与 7.12.x 行为一致。
具体建议,按优先级:
- 恢复 TCP 通道,或至少在 HTTP 失败后回退到 TCP。这是最直接的修复,实测 3/3 成功。
- 如果要保留纯 HTTP 方案,11 秒的单请求超时对慢出口链路过于激进 —— 1MB 的块在 3 Mbps 链路上理论最快就要 2.7 秒,叠加并发 3 后单请求的实际耗时很容易逼近甚至超过 11 秒。建议改成可配置,或按测得的吞吐动态计算。
102902 应纳入重试。目前重试白名单只有 request timeout / read ECONNRESET / not a valid proto,错误码直接抛出,不换服务器也不降级。
- 把 Highway 的错误明细透传到 OneBot 响应,不要一律包装成「请重试」。
另外提一个 7.x 里存在的小缺陷,恢复 TCP 通道时值得一并处理:v7 的 try { TCP } catch { HTTP } 两条路径复用同一个 readable。若 TCP 传到一半失败,流已被部分消费,回退 HTTP 时会从中间继续读,产出的文件是残缺的。正确做法是每次尝试都重建 createReadStream。
LLBot 运行日志
# ---------- 原版 8.2.1(HTTP 通道),三次连续失败 ----------
第1次 offset 2 MiB
[E] onebot11-adapter 发生错误 Error: [Highway] httpUpload Error uploading block at offset 2097152: Highway request timeout (11s) on http://157.148.54.88:443/cgi-bin/httpconn?htcmd=0x6FF0087&uin=<uin>
at upload (/root/LLBot-CLI-linux/bin/src/ntqqapi/helper/highway.ts:370:17)
at async HighwayHttpSession.uploadBlockWithRetry (/root/LLBot-CLI-linux/bin/src/ntqqapi/helper/highway.ts:374:5)
at async worker (/root/LLBot-CLI-linux/bin/src/ntqqapi/helper/highway.ts:290:11)
at async HighwayHttpSession.upload (/root/LLBot-CLI-linux/bin/src/ntqqapi/helper/highway.ts:302:5)
第2次 offset 8 MiB
[E] onebot11-adapter 发生错误 Error: [Highway] httpUpload Error uploading block at offset 8388608: Highway request timeout (11s) on http://157.148.36.32:443/cgi-bin/httpconn?htcmd=0x6FF0087&uin=<uin>
第3次 offset 41 MiB
[E] onebot11-adapter 发生错误 Error: [Highway] httpUpload Error uploading block at offset 42991616: HTTP Upload failed with code 102902
# ---------- 补回 TCP 通道后,三次连续成功 ----------
[W] highway [TCP-PATCH] 群文件经 TCP 通道上传成功,耗时 152.2s
[W] highway [TCP-PATCH] 群文件经 TCP 通道上传成功,耗时 153.1s
[W] highway [TCP-PATCH] 群文件经 TCP 通道上传成功,耗时 152.8s
OneBot 客户端运行日志
# ---------- 原版 8.2.1 ----------
=== HTTP : 连续 3 次,每次全新随机文件 ===
[22:38:34] 第1次 文件已生成 md5=7a59998a1b62
[22:40:40] 第1次: 失败 125.9s retcode=1200 [Highway] httpUpload Error uploading block at offset 2097152: Highway request timeout (11s)
[22:40:45] 第2次 文件已生成 md5=f0b646228df7
[22:43:07] 第2次: 失败 141.6s retcode=1200 [Highway] httpUpload Error uploading block at offset 8388608: Highway request timeout (11s)
[22:43:12] 第3次 文件已生成 md5=b9347d5bc568
[22:45:30] 第3次: 失败 137.7s retcode=1200 [Highway] httpUpload Error uploading block at offset 42991616: HTTP Upload failed with code 102902
真实成功 0 / 失败 3 / 共 3
# ---------- 补回 TCP 通道后 ----------
=== TCP : 连续 3 次,每次全新随机文件 ===
[22:28:38] 第1次 文件已生成 md5=74f2532f980e
[22:31:11] 第1次: 成功 153.3s retcode=0
[22:31:18] 第2次 文件已生成 md5=72bde9d3bff2
[22:33:52] 第2次: 成功 154.8s retcode=0
[22:33:59] 第3次 文件已生成 md5=f35a14eca0d9
[22:36:33] 第3次: 成功 154.3s retcode=0
真实成功 3 / 失败 0 / 共 3
## 附:无法通过降级规避
考虑到 8.x 的这个回归,使用者理论上可以退回 7.12.x,但这条路目前是断的:
- v7 的 PMHQ(v7.3.2)不内嵌任何 QQ 版本的偏移数据,运行时从 `http://pmhq.luckylillia.com/a.bin` 获取,而**该域名目前解析为 NXDOMAIN**,导致 7.12.15 配当前版本 QQ 直接报「未找到当前 QQ 版本 (52892) 的偏移数据」而无法启动。
- 尝试把 8.x 的 PMHQ v8.1.1 换入 7.12.15:注入本身成功(能正确识别 QQ 3.2.33-52892),但 `llbot.js` 与注入器之间的 WS 协议跨大版本不兼容,表现为 `checkLogin` 读到 undefined、`pmhq ws send: wait result timeout`、`/api/login-qrcode` 返回 500。
也就是说受影响的用户没有可行的回退方案,只能等待修复或自行修改构建产物。
(另:`api-auth.luckylillia.com` 的启动预检从中国大陆访问存在间歇性超时,重试通常可通过,与本 issue 无关,顺带一提。)
系统版本
Debian GNU/Linux 13 (trixie) x86_64,腾讯云轻量应用服务器(ap-guangzhou),出口带宽 3 Mbps
QQNT 版本
3.2.33-52892
LLBot 版本
8.2.1(有头模式 ./llbot --pmhq,PMHQ v8.1.1)
OneBot 客户端
为排除框架干扰,测试直接用 ws 客户端连 LLBot 的正向 WebSocket(127.0.0.1:3001)调用 upload_group_file,未经过任何机器人框架。 原始问题也稳定复现于 TRSS-Yunzai 3.1.3。
发生了什么?
从 7.12.x 升级到 8.x 之后,
upload_group_file上传较大文件(本次测试 50MB)稳定失败;7.12.x 时期同样的文件可以正常上传。定位到
src/ntqqapi/api/file.ts的uploadGroupFile:8.x 删除了HighwayTcpSession,只保留 HTTP 分块上传。v7.12.15
v8.2.1
src/ntqqapi/helper/highway.ts里HighwayTcpSession类也整个不存在了(文件从 192 行增至 472 行,同时新增concurrency = 3与每请求timeout: 11 * 1000)。为验证这是否就是直接原因,我在 8.2.1 的构建产物里照 v7 的实现补回了一个
HighwayTcpSession,并把uploadGroupFile改回「TCP 优先、失败回退 HTTP」,然后做了对照实验。对照实验结果
条件:同一台机器、同一个群、同一份 OneBot 调用;每一轮都重新生成一个全新的 50MB 随机文件(
head -c 52428800 /dev/urandom,md5 各不相同,避免服务端fileExist秒传导致上传分支被跳过);两组在同一个半小时内完成。Highway request timeout (11s)Highway request timeout (11s)HTTP Upload failed with code 102902TCP 3/3 成功且耗时高度一致(153~155 秒,正好是 50MB 跑满 3 Mbps 出口的时间);HTTP 0/3。
两点补充观察
1. 失败位置是随机的,不存在固定阈值
三次分别断在 2 MiB、8 MiB、41 MiB。我最初在另一台机器上连续四次都断在
offset 42991616,一度以为存在「41 MiB 硬上限」,但扩大样本后确认那只是巧合。这与 #829 报告的「50多M-60多M的任意大小处断开」一致。2. OneBot 层返回的
retcode=1200 请重试会掩盖真实原因三次失败在 OneBot 层都是
{"status":"failed","retcode":1200,"wording":"请重试"}。由于测试文件是纯随机二进制(不含任何可被内容审核的信息)却同样触发,可以确认这个提示与内容审核无关,真正原因只体现在 LLBot 日志的 Highway 明细里。建议把 Highway 的错误信息透传到 OneBot 响应中,否则使用者很容易误判为账号风控或内容问题。关于 #829
#829 报告的也是
102902,被标记为「v8.1.4 已修复」并关闭。但查修复提交def580ed的 diff,实质改动只有两行:其余为代码格式调整。这降低了触发概率但未消除问题,在 8.2.1 上仍然稳定复现(本次 0/3)。
如何复现
fileExist会秒传,根本不会走 Highway):head -c 52428800 /dev/urandom > /tmp/test-50MB.bin{ "action": "upload_group_file", "params": { "group_id": 123456789, "file": "/tmp/test-50MB.bin", "name": "test-50MB.bin" } }在 3 Mbps 出口的机器上 3/3 失败。出口带宽越低、单次上传耗时越长,越容易触发。
期望的结果?
upload_group_file能稳定上传大文件,与 7.12.x 行为一致。具体建议,按优先级:
102902应纳入重试。目前重试白名单只有request timeout/read ECONNRESET/not a valid proto,错误码直接抛出,不换服务器也不降级。LLBot 运行日志
OneBot 客户端运行日志