乐于分享
好东西不私藏

火山引擎云服务器OpenClaw升级奇遇记

火山引擎云服务器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 是清白的——它的 AcceptDNSfalse,根本没有接管 DNS;routing table 52 里也查不到 100.96.x.x 的路由。

真相:火山引擎这台 VM 的 DHCP 服务器下发了两个不可达的 DNS 地址。可能是这个网段的配置问题,但它导致了整个域名解析的瘫痪。服务器并不是没有互联网——用 IP 直连是通的:

curl https://104.16.6.34 -k
# <html>... 403 Forbidden ...  ← 通了!

修法:配置 systemd-resolved8.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

Ff 的顺序写反了。

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 —