Claude Code 通过中转站频繁报 524 的排查记录
2151 字约 8 分钟
摘要Claude Code 经过中转站访问模型,会话偶尔卡住并报 Cloudflare 524。先发现它一次都不重试,改配置后又出现重试 45 分钟全部失败,我先后听到两个解释,其中“上下文太大”被日志里的数据推翻了。
我的 Claude Code 不是直连官方接口,而是通过一个中转站访问模型,中转站前面还挂着 Cloudflare。平时用着没什么感觉,直到那几天,会话开始随机卡住,最后弹出一大段英文报错,核心是一行 Error 524: A timeout occurred。
524 是 Cloudflare 自己生成的错误,意思是它已经连上了后面的服务器,但服务器在 120 秒内没有返回任何数据,它就把连接断了。也就是说,我的电脑到 Cloudflare 是通的,问题出在中转站或者它再往后的地方。
先发现它一次都不重试
最早的两次 524,会话都是在一次请求之后就停了,我得手动输入“继续”才能接着干活。Claude Code 对 5xx 错误本来是会自动重试的,最多 10 次,这次怎么没有。
助手先翻了会话记录,请求发出约 140 秒之后报错,整个过程只请求了一次,没有任何重试的痕迹。前一天的那次 524 也是这样。接着它去 Claude Code 程序本体里翻了翻相关的字符串,当时的版本是 2.1.302,看到了这样的逻辑。
- 重试前要等多久,取服务端建议的等待时间和自己的退避时间中较大的一个。
- Cloudflare 的 524 响应里带着“建议 120 秒后再试”。
- 程序规定,建议等待超过 60 秒的错误,直接当作不可重试,结束这一轮。
于是一个本该重试的超时,被一个“建议等太久”的字段变成了立刻放弃。
程序里有一个给远程运行场景用的持久重试模式,遇到需要长时间等待的错误会等完再试。助手在 settings.json 的 env 里加了三项。
| 变量 | 作用 |
|---|---|
CLAUDE_CODE_RETRY_WATCHDOG=1 | 打开持久重试,遇到要等很久的错误时等完再重试 |
CLAUDE_CODE_MAX_RETRIES=10 | 重试次数仍然是 10 次,这个模式默认是 300 次 |
CLAUDE_CODE_RETRY_WATCHDOG_MAX_WAIT_MS=1800000 | 遇到限流或过载时,最多等 30 分钟 |
这几个开关没有出现在官方文档里,以后升级可能失效,所以助手特意提醒,如果哪天 524 又让会话停下,先看这几个变量还在不在。同时它也说明了代价,每次 524 要卡两分钟多,再等两分钟重试,如果中转站一直不好,最久要四十多分钟才会最终报错。
这是对着一个具体版本的发现上面的内部逻辑来自对 2.1.302 的观察,不是公开的接口约定,换一个版本可能就不是这样了。
重试起作用之后,又卡了很久
改完配置的当天夜里,我在一个很长的会话里被卡住了。它的上下文已经有约 54 万 token,最后一次正常回复之后,下一个请求怎么也拿不到回应。这回重试确实起作用了,客户端大约每 4 分 20 秒重试一次,一共试了 4 次,都失败。我按 Esc 之后发了一句“继续”,反而更慢。
助手给出的第一个解释很自然,上下文太大了。请求越大,上游开始回复就越慢,更容易超过 Cloudflare 的 120 秒。它还解释了为什么“继续”更糟,这时 AGENTS.md 这类说明文件被重新读进来,内容和之前有出入,提示缓存全部失效,54 万 token 要从头处理。/compact 也救不了,因为压缩要把整段上下文都发出去给模型做总结,同样会卡住。最后只有 /clear 才让这个会话恢复。它因此建议,上下文到 20 到 30 万 token 就手动压缩。
我当时对图片也有怀疑,因为以前好像遇到过图片数量上限的提示。助手去核对了,那个会话压缩后只有 27 张图,离 100 张的上限很远,每张都不超过 2000 像素,base64 之后总共约 8.6MB,也在请求体积限制之内。图片的怀疑被排除了。
第二天凌晨,另一个反复截图调整海报的会话也卡住了,这次上下文约 46.5 万 token,里面累积了 60 张左右截图。Claude Code 在后台自动重试了 10 次,每次也是约 4 分 20 秒,前后 45 分钟,最后弹出 524。助手把同一个解释又说了一遍。
我觉得这个解释没有道理
我直接反驳了它。我有时候上下文比这还大,也能发出去。120 秒没有回复本身就不合理。为什么我手动压缩一下它又回复了?
这几个质疑都很到位,助手随后把本机所有会话的日志翻了一遍,用数据来检验自己的说法。
- 上下文超过 40 万 token 的请求,成功了 775 次,最大的约 56.7 万,比出问题那次的 46.5 万还大。
- 带的截图比那次更多更大的请求,也成功过几百次。
- 出事的那 45 分钟里,我另一个会话(上下文约 32 万)走同一个中转站,成功了 55 次。所以中转站整体是好的,坏的只有这一个会话。
- 那个会话卡了 45 分钟后停下,6 个小时后我发了“继续”,触发的自动压缩成功了。压缩要把整段上下文发出去,请求一点也不小,能成功,说明请求本身没问题,是这 6 个小时里别的东西变了。
“上下文太大”这个解释被推翻了,之前建议的“20 到 30 万就压缩”也跟着作废。
更可能的原因,仍然没有证实
推翻一个解释,并不等于找到了答案。助手提出了两个新的猜测。
第一个是中转站会把同一个会话的请求固定发到同一个上游账号,这样能命中提示词缓存。如果这个账号卡住了,这个会话的所有请求都会失败,换一个会话就好了,/clear 换了会话编号也能恢复。但中转站的运营者告诉我,他只有一个上游账号,而且同一时间另一个会话成功了 55 次,说明账号本身是好的。这条猜测基本可以放弃。
第二个是卡住的是这段对话的前缀。同一个对话每次发出去,开头一大段内容都完全一样,为了复用缓存,请求很可能被送往同一个地方处理。那里如果卡住了,所有以同样内容开头的请求都会跟着卡,包括重试、继续发消息,甚至 /compact。/clear 之后开头变了,就恢复了。缓存的有效期大约一小时,我隔了 6 个小时再发,缓存早就过期了,又能用了。
所有现象都对得上这个解释,但我们证明不了。要确认,只能看中转站的后台日志,也就是那个时间段这个会话的请求被分到了哪里、上游返回了什么。助手建议我把 Cloudflare 报错里的请求编号和时间发给中转站的运营者。它还排除了几个其他嫌疑,Claude Code 是从本机直连中转站的,没有经过任何代理,也没有经过我的论坛服务器,我本机的网络也是通的。
遇到的时候怎么办
在没有更多证据的情况下,我只总结了一条经验。
当一个会话一直转圈、最后报 524,而其他会话都正常,就不要等它把 10 次重试耗完了,直接 /clear 或者开一个新会话。如果需要保留进度,先在新会话里交代一下做到了哪里。这比等 45 分钟有用得多。
关于计费,我只能根据中转站项目公开的说明推断。它按上游返回的用量记账,没有拿到任何返回的请求没有用量,不会产生扣费记录。流已经开始、中途断掉的请求,则按已收到的部分计费。想确切知道,要看中转站后台的使用记录。
这篇记录里有一个被推翻的结论我特意把被推翻的那个解释也写了进来。排查这类间歇性故障时,能自圆其说的解释很容易被当成答案,真正有用的是拿全部日志去数一数,有多少反例。