火山引擎云服务器OpenClaw升级奇遇记
一台火山引擎云服务器的”升级奇遇记”:五个坑、一个上午、终于跑通了
本文记录了一次真实的运维排障过程,把踩过的坑、绕过的弯儿,原原本本讲给你听。
开场白:一个简单的需求
事情的起因很简单:把一台部署了 OpenClaw 的火山引擎云服务器的 AI 后端,从 OpenAI 换成 Anthropic(Claude)。
听起来不就是改个 API Key 的事儿吗?
太轻松了。
一个上午过去,踩了整整五个坑,才让 “hi” 从微信发出去、OpenClaw正儿八经地回了一句话。
第一坑:DNS 坏了,但没有任何人告诉你
配好 Anthropic provider,满怀期待地重启 gateway,微信发一条消息:
run error: LLM request failed.
先检查网络。
curl https://api.anthropic.com
# curl: (28) Resolving timed out after 8000 milliseconds
DNS 解析超时。
查 /etc/resolv.conf,发现 nameserver 是两个 100.96.x.x 的地址。
nslookup google.com
# ;; communications error to 100.96.0.2#53: timed out
这两个 DNS 服务器根本不响应。
怀疑对象一号:Tailscale。 因为 100.64.0.0/10 是 Tailscale 的 CGNAT 范围,100.96.x.x 落在里面,会不会是 Tailscale 把 DNS 流量吃掉了?
tailscale debug prefs | grep DNS
# "CorpDNS": false
Tailscale 是清白的——它的 AcceptDNS 是 false,根本没有接管 DNS;routing table 52 里也查不到 100.96.x.x 的路由。
真相:火山引擎这台 VM 的 DHCP 服务器下发了两个不可达的 DNS 地址。可能是这个网段的配置问题,但它导致了整个域名解析的瘫痪。服务器并不是没有互联网——用 IP 直连是通的:
curl https://104.16.6.34 -k
# <html>... 403 Forbidden ... ← 通了!
修法:配置 systemd-resolved 走 8.8.8.8,同时修改 netplan 让 DHCP 不再覆盖 DNS,再禁掉 cloud-init 的网络管理防止重启被重置。
# /etc/netplan/01-netcfg.yaml
network:
ethernets:
eth0:
dhcp4: yes
dhcp4-overrides:
use-dns: false # ← 关键
修好之后,nslookup registry.npmjs.org 秒出结果,npx 也能正常装包了。
第二坑:API Key 手打,大小写写反了
DNS 修好之后,再测 Anthropic API:
curl https://api.anthropic.com/v1/models -H 'x-api-key: sk-ant-...'
# {"data": [...]} ✓
好,API 是通的。那为什么 openclaw 还是报错?
翻 session trajectory 文件:
"terminalError": "non_deliverable_terminal_turn"
"input": 0,
"output": 0
input 和 output 都是 0。这说明请求根本没发出去——在客户端就失败了。
检查 openclaw 配置文件里实际存的 API key 末尾字符:
配置文件里:...UDuOdkzVQ-Ffd_bwAA
正确的 key:...UDuOdkzVQ-fFd_bwAA
F 和 f 的顺序写反了。
Anthropic API key 是 base64url 编码,大小写完全敏感。一个字符之差,401 鉴权失败。
openclaw config set models.providers.anthropic.apiKey 'sk-ant-...(正确的key)'
第三坑:maxTokens 的幽灵值
key 对了,还是报同样的错。
继续看报错信息:
Anthropic Messages transport requires a positive maxTokens value for anthropic/claude-sonnet-4-6
openclaw config get models.providers.anthropic.models 明明显示:
{"id": "claude-sonnet-4-6", "maxTokens": 8192}
有值啊?
直接读原始 JSON 文件:
import json
cfg = json.load(open('/root/.openclaw/openclaw.json'))
for m in cfg['models']['providers']['anthropic']['models']:
print(m['id'], m.get('maxTokens'))
# claude-sonnet-4-6 None ← !!!
None!
openclaw config get 展示的是运行时补全的默认值,而实际文件里存的是 null。新版本 2026.6.1 的 Anthropic transport 对此更严格,不允许使用默认值,必须文件里就有明确的正整数。
修法:在 config set 时把 maxTokens 显式写进去:
{"id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6", "maxTokens": 32000}
第四坑:/etc/hosts 绕路,被 openclaw 安全模块拦截
(这个坑其实出现在 DNS 修好之前,这里按逻辑顺序讲)
DNS 还没修好时,想了个临时方案:把 api.anthropic.com 写进 /etc/hosts,指向另一台有外网的内网服务器,再在那台服务器上用 iptables 做透明转发。
100.xx.xx.xx api.anthropic.com
然后发现 API 调用还是 input=0 失败。打开 debug 日志:
[security] blocked URL fetch (url-fetch) targetOrigin=https://api.anthropic.com
reason=Blocked: resolves to private/internal/special-use IP address
openclaw 自己有安全检查。它会 resolve URL 的 IP,如果发现是私有地址(RFC1918、Tailscale CGNAT、loopback 等),直接拒绝——不管你的 hosts 怎么写。
这个设计是合理的安全防护,但在我们的绕路方案里,它成了拦路虎。
解决方案:既然 DNS 已经修好、服务器本来就能直连公网,把 hosts 绕路整条删掉就行了:
sed -i '/api.anthropic.com/d' /etc/hosts
第五坑:Session 历史污染,越修越坏
每次修一个问题、重试一次,都要在 TUI 里发一条 “hi”。
前几次失败的 “hi” 留下了这样的对话历史:
用户: hi
助手: [assistant turn failed before producing content] ← 失败占位符
用户: hi
助手: Anthropic Messages transport requires a positive maxTokens value... ← 错误信息
用户: hi ← 当前
问题来了:这段历史被一起打包发给 Anthropic API。上面有无效的 assistant 内容,有连续的 user 消息……Anthropic 拒绝了请求,openclaw 又记录一次失败,历史又多一条错误。
越修越坏,循环不止。
解法:直接清空 session 历史文件:
for f in glob.glob(session_dir + "/*.jsonl"):
if "trajectory" not in f:
open(f, "w").close()
重启 gateway,历史归零,重新开始。
升级插曲:2026.4.22 → 2026.6.1
中间还顺带把 openclaw 从 2026.4.22 升到了最新版 2026.6.1:
npm install -g openclaw@2026.6.1
openclaw gateway restart
升级很顺滑,自动迁移了旧的 task registry 格式。但正是这次升级暴露了第三坑(maxTokens: null)——旧版对此容忍,新版直接报错。版本升级本身没问题,只是把之前潜伏的配置缺陷翻出来了。
终于!
五个坑都填完之后,TUI 里发出”hi”:
Hey! How's it going? 😊
微信里发出”你好”,Claude 用中文回复了。
整个排障过程大概是这样的思维链:
报错 LLM request failed
→ 检查网络 → DNS 不通
→ 以为是 Tailscale → 证伪 → DHCP 问题 → 修 DNS
→ API 通了,openclaw 还失败
→ input=0 说明没发出请求
→ key 大小写写反 → 修 key
→ maxTokens=null → 修配置
→ hosts 绕路被安全模块拦 → 删 hosts
→ session 历史污染 → 清 session
每一个问题都隐藏得不那么明显,每一个错误信息都需要多走几步才能找到根源。
几个小结论
1. openclaw config get 不等于文件里的值。 它会补默认值,某些版本不容忍文件里有 null。重要配置直接读 JSON 文件确认。
2. /etc/hosts 改域名指向内网 IP,会被 openclaw 安全模块拦截。 这是有意为之的安全设计,不是 bug。
3. 排查网络问题,先分开测试 DNS 和 TCP 连通性。 DNS 不通不代表 TCP 不通,直接用 IP 测是个好习惯。
4. Session 历史是有状态的。 LLM 调用失败后 session 里会残留错误占位符,会影响下一次调用。调试阶段遇到奇怪的重复失败,优先清一下 session。
5. 版本升级是好事,但可能暴露旧配置的潜伏问题。 升级前最好对照新版 changelog 检查一遍配置。
OpenClaw 是一个开源的 AI agent 框架,支持微信、企业微信、飞书等多个渠道接入,可以运行在云服务器、Mac等环境上。如果你也在折腾类似的部署,欢迎留言交流。
— END —
夜雨聆风