如果你最近在折腾 OpenClaw——社区里习惯叫它“龙虾”——十有八九会被一个报错卡住:disconnected (1008): unauthorized: gateway token。我第一次遇到是在 Windows 上用 WSL2 部署的时候,CLI 刚起来,任务还没跑两秒,终端就弹出这么一行,随后进程直接退出。当时网上一搜,一半结果在讲“你是不是 API key 写错了”,另一半在讲“网络断了重连”,跟这个 1008 完全对不上。这篇就把我这次完整的排查链路写下来,包括 1008 到底是什么、gateway token 从哪里来、怎么生成和写入、如何验证,以及几个经常一起出现的“亲戚”报错怎么区分。无论你是刚部署 OpenClaw 的新手,还是升级后突然报这个错的老用户,都可以直接照着往下查。
1. 先分清“龙虾”在哪条链路上咬人:报错现场与三层结构
1.1 报错现场长什么样
先看报错本身。常见几种形态:
$ openclaw run "帮我整理一个周报" [INFO] openclaw version 0.4.2, platform linux-x64 [INFO] connecting to gateway wss://gw.openclaw.local:8443/v2 [ERROR] disconnected (1008): unauthorized: gateway token也有时候报错会包装成任务失败的样子:
error running remote compact task: stream disconnected before completion: unauthorized (1008): gateway token不论哪种形式,关键信息都是三个:disconnected、1008、unauthorized: gateway token。disconnected表示长连接被断开;1008是断开时的状态码;后面的字符串是网关给出的拒绝原因。它不是乱码,而是网关直接告诉你:“我没验证过你的 gateway token,所以我不放你进来。”
1.2 三层连接:客户端、网关、模型
要理解这个报错,先得把 OpenClaw 的连接链路理顺。它的基础结构可以简化成三层:
- 客户端:你敲命令的 CLI,或者跑着任务的编排进程。
- 网关:负责转发指令、管理长连接、做统一认证的中间层。它可以是官方托管的网关,也可以是你自建的网关服务。
- 模型服务:真正干活的推理服务,比如本地 Ollama、通过 OpenAI 兼容接口托管的模型等。
每一跳都用不同的凭证:
- 客户端到网关:用 gateway token,验证“你这个客户端有没有权限使用这个网关”。
- 网关到模型服务:用模型 API key,验证“网关有没有权限调用模型”。
也就是说,哪怕你本地的模型 API key 完全正确,一旦网关不认你的 gateway token,依然会在第一跳被卡住。很多同学第一反应是去检查自己的模型密钥,完全忽略了还有网关这一层,于是来回改配置都无效。
1.3 为什么网关不直接给你返回 401
这里有一个容易困惑的点:REST 接口如果没权限,通常会返回 HTTP 401。但 WebSocket 建立连接时,网关会先完成 HTTP Upgrade 握手,再在握手完成后立刻用关闭帧断开。也就是说,认证失败发生在 WebSocket 协议层,而不是 HTTP 状态码层。所以你看到的是disconnected (1008),而不是401 Unauthorized。
这也解释了为什么你在浏览器开发者工具里可能看到“WebSocket connection failed”一类的提示,而不是一个明确的 401。协议层把“认证失败”这件事表达成了“连接被策略拒绝”,这在排错时很容易让人觉得是网络问题。
2. 1008 这个状态码的门道:策略违规、无效令牌和绑定关系
2.1 RFC 6455 里的 policy violation
WebSocket 标准协议 RFC 6455 定义了一组关闭状态码,1008 官方含义是 Policy Violation。简单说,服务端认为“你的连接请求违反了它的策略”,于是主动关断。它和 1006(连接异常中断,没有收到关闭帧)、1011(服务端内部错误)都不一样。
打个比方:REST 世界的 401 像门禁读卡器说“你卡无效”;WebSocket 的 1008 则像门禁读卡器说“你根本没权限进这栋楼”。门没坏,网络也通,纯粹是这扇门不给你进。
2.2 gateway token 失效的常见原因
结合我在 OpenClaw 社区和自身环境里看到的案例,gateway token 失效可以归结为下面几类原因:
| 原因 | 现象 | 常见动作 |
|---|---|---|
| token 缺失 | 配置里没写,或环境变量没加载 | 补写配置 |
| token 复制不完整 | 从网页控制台复制时漏了字符 | 重新复制 |
| token 带引号/换行 | .env 里用了引号,shell 把引号也读进去了 | 去掉引号 |
| token 过期 | 有的 token 默认 30 天有效 | 重新生成 |
| token 与客户端绑定不匹配 | 生成的 token 绑定了固定设备 ID | 重新生成并绑定当前设备 |
| 网关地址不匹配 | 官方网关 token 配到了自建网关上 | 确认网关地址 |
| token 权限不足 | token 只授权了某个 skill,但任务调用了别的范围 | 提升权限或重新签发 |
我在后面第 3 章的排查过程里遇到的就是“带引号/换行”这一类。这是最容易被忽略、也最浪费时间的坑。
2.3 为什么别人电脑上能跑,你这里跑不通
如果你拿着一个在朋友电脑上能正常运行的配置,放到自己机器上却报 1008,不要急着觉得是系统不兼容。很大概率是 gateway token 和客户端之间存在绑定关系。OpenClaw 的网关在生成 token 时,可以选择绑定客户端指纹、设备 ID 或来源 IP。这么做是为了防止 token 被复制到别的机器上滥用。
所以,自己机器上的正确姿势是:在本机重新生成一个 token,而不是复制别人的。当然,绑定关系也可以在生成时关掉,但我不建议图省事关掉,尤其是当你的网关暴露在公网的时候。
3. 我这次的完整排查过程:从日志到根因,一步步来
3.1 第一件事:确认是必现还是偶发
遇到报错先别急着改配置。我习惯先连跑三次同样的任务,看报错是否必现。如果三次里只有一次报 1008,优先级最高的是检查网关是否重启、网络是否有抖动;如果每次必现,才进入配置排查。
我这次的情况是必现,每次启动都放在同一行报错。这就把范围缩小到了“网关认为 token 有问题”,而不是偶发的网络中断。
3.2 按加载顺序逐个排查配置来源
OpenClaw 的配置加载顺序一般是:CLI 参数 > 环境变量 > 配置文件 > 内置默认值。这意味着,如果你在 CLI 参数里传了一个错误 token,那环境变量里写得再对也白搭。所以排查时要按这个顺序反着来:
- 先看 CLI 启动脚本里有没有
--gateway-token之类的参数; - 再看环境变量里有没有
OPENCLAW_GATEWAY_TOKEN,用env | grep -i openclaw看; - 再看
~/.openclaw/config.yaml或.env文件; - 最后确认有没有系统服务(systemd、Windows 计划任务)在启动时覆盖了你的环境变量。
我这次是在一台 Ubuntu 服务器上部署,用 systemd 托管。一开始我看 systemd 服务文件里没写 token,但服务能起来,就以为没影响。后来才发现 systemd 服务默认不读取用户 shell 的环境变量,我在终端里export的OPENCLAW_GATEWAY_TOKEN根本没被服务拿到。这是很多自托管用户会踩的坑。
3.3 我的根因:.env 里的引号和隐藏字符
把 systemd 的环境变量问题解决后,报错依然在。我这才开始怀疑 token 本身。当时我用的.env文件是从网页控制台的“复制配置”按钮生成的,里面长这样:
OPENCLAW_GATEWAY_TOKEN="oclw_xxxxxx"看着很正常,但问题恰恰出在这对双引号上。OpenClaw 的配置解析器在读取.env时,默认会保留引号作为值的一部分,而我在终端手动 export 时,shell 又会把引号吃掉。两种读法得到的结果不一致,网关那边自然不认。
为了确认,我执行了:
echo "$OPENCLAW_GATEWAY_TOKEN" | od -c结果一眼就看清了:值首尾各多出一个",末尾还有一个看不见的换行。也就是说,发送给网关的 token 实际是"oclw_xxxxxx"(带引号),而不是oclw_xxxxxx。
去掉引号、确保.env文件里每个变量独占一行、结尾没有多余字符,再重新启动服务,报错立刻消失。整个过程大概花了四十分钟,实际根因就是一个引号。
3.4 排除“时间漂移”和“证书问题”
这里补充一个排查思路:如果 token 完全正确,但仍然报 1008,我建议顺手看看系统时间和网关时间是否一致。很多签名型 token 是带时效的,本机时间如果偏了好几分钟,网关会判定 token 已过期,同样以 1008 关闭连接。Linux 下用timedatectl status确认 NTP 同步状态,Windows 下用“设置 -> 时间与语言 -> 自动设置时间”确认。
另外,如果你的网关是自建的,并且走 HTTPS/WSS,证书链不完整也会导致握手异常。但这种情况通常会报证书错误而不是 1008,所以优先级往后放。
4. 修复 gateway token 的标准操作:生成、写入、验证三板斧
4.1 生成 token:CLI 和控制台两种方式
不同版本的 OpenClaw 命令略有差异,以你本版openclaw gateway token --help为准。我惯用的命令是这样的:
openclaw gateway token create --name my-dev --expires 30d执行后命令行会输出一个类似oclw_xxxxxxxx的 token,并且只显示这一次。请立刻保存到你的密钥管理器里,不要贴在聊天工具中。如果你用的是官方托管网关,也可以登录网页控制台,在 Gateway Tokens 页面手动生成,还能选择绑定设备或绑定 IP。
CLI 方式和控制台方式的差异在于:CLI 生成后通常直接写进当前用户配置目录,控制台生成则需要你自己复制回本地配置。
4.2 写入配置:环境变量还是配置文件
我推荐的做法是优先使用环境变量,因为环境变量不会因为不同平台的配置路径差异而失效。示例:
export OPENCLAW_GATEWAY_TOKEN="oclw_xxxxxxxx"如果你要长期使用,就写入.env文件:
OPENCLAW_GATEWAY_TOKEN=oclw_xxxxxxxx注意:不要加引号,不要带注释,不要在行尾留空格。
如果你更习惯用配置文件,~/.openclaw/config.yaml里的写法类似:
gateway: token: oclw_xxxxxxxx但请注意,OpenClaw 读取配置时会区分“字符串”和“带引号的字符串”。YAML 里如果写成token: "oclw_xxx"一般没问题,YAML 解析器会把引号当作字符串定界符;但.env格式没有这套规则,所以.env里一定不要加引号。
4.3 验证 token 是否真的生效
写完之后不要直接跑大任务,先用一个轻量命令验证:
openclaw gateway status如果输出里能看到connected或authorized,说明网关这一跳已经通了。如果状态没变化,打开 debug 日志再看一遍握手过程:
OPENCLAW_LOG_LEVEL=debug openclaw run "ping"debug 日志里,成功时会看到类似gateway authentication ok的记录;失败时会保留网关返回的关闭帧内容,方便你确认是 token 问题还是其他问题。
4.4 如果还不行,按这张表继续查
| 现象 | 原因 | 操作 |
|---|---|---|
| 能 ping 通网关但 1008 | token 权限不足或绑定不符 | 重新生成一个默认全权限 token |
| 网关地址连不上 | 地址写错或端口不通 | 核对 config 里的 gateway URL |
| 刚改完配置仍报错 | 旧进程还在跑 | 杀掉进程重启,确认加载了新配置 |
| 局域网/公网访问异常 | 代理或防火墙拦截 WSS | 临时关代理再试 |
| 时间不对导致 token 过期 | 系统时钟漂移 | 开启 NTP 同步后再试 |
5. 热搜里那些“亲戚报错”:一眼分清是同一毛病还是新问题
在查资料的过程中,我发现很多人会把这几种报错混在一起聊。它们看起来都像“连接断了”,实际上原因完全不同,修法也不一样。
5.1 stream disconnected before completion 系列
OpenClaw 任务跑远程 skill 时,经常会出现这类带stream disconnected before completion的报错,后面跟着的具体原因各不相同:
stream closed before response.completed:通常是因为模型服务提前断开了 SSE 流,比如输出达到上限或服务端超时。transport error: network error:多见于网络不稳定或网关负载高,偶发为主。idle timeout waiting for sse:长时间没有新的数据帧到达,网关主动断开。可以检查模型端是不是卡在排队上。由于目标计算机积极拒绝,无法连接:这个一般是本地端口没监听,比如自建网关没启动。
这一类的共同点是:连接已经建立,但数据传输过程出了问题;而 1008 则是连接建立阶段就被拒绝。两者在日志里出现的位置不一样,修复思路也不一样。遇到这类问题,我一般先看日志里有没有 “connect to model service” 成功记录,再决定查网关还是查模型。
5.2 adb unauthorized 怎么解决
OpenClaw 如果要调度安卓设备,会用到 ADB。很多人在连接手机时报adb unauthorized,这个报错和 gateway token 没有关系,它表示 ADB 服务端已经发现设备,但设备端没有授权当前电脑的 RSA 指纹。解决办法很直接:手机屏幕上会弹出一个“允许 USB 调试”的对话框,点允许并勾选“一律允许”;如果没有弹窗,在电脑上执行adb kill-server && adb start-server再插入设备。
5.3 WSL2 环境验证
如果你的 OpenClaw 装在 Windows 的 WSL2 里,启动时报“无法安全验证 WSL2 环境”这类提示,最常见的是 WSL 内核版本过低。在 PowerShell 中运行:
wsl --status如果提示内核需要更新,就执行wsl --update,然后重启 WSL。另外要注意 Windows 侧防火墙对 WSL 虚拟网卡的拦截,尤其是当你用localhost访问自建网关时,WSL2 的 NAT 网络可能会把 localhost 映射成不同的地址。遇到连不上,试着改用 WSL 的虚拟 IP 访问,或者用wsl hostname -I查看地址。
5.4 401 API key 和 1008 gateway token 的区分
这里单独把两个最像的报错列出来:
unexpected status 401 unauthorized: incorrect api key provided: sk-xxxx和
disconnected (1008): unauthorized: gateway token前者出现在“网关调用模型服务”这一跳,关键词是incorrect api key,后面跟的 key 是模型服务的 API key。修法是去模型服务控制台重新生成 key,并检查网关配置里的api_key或model_provider字段。
后者出现在“客户端连接网关”这一跳,关键词是gateway token。修法是按第 4 节的流程重新生成并配置 gateway token。
两个报错一字之差,一个在网关和模型之间,一个在客户端和网关之间,排查方向完全不同。我见过有人因为 401 报错反复重置系统,其实只要换一个 API key 就好了;也见过有人因为 1008 报错反复检查 API key,结果问题只是.env里的一个引号。
6. 几个我长期养成的实操习惯
最后分享几个我自己用着很顺手的习惯,算是给还没被这只“龙虾”咬过太多次的朋友一点预防针。
第一个习惯是统一用.env管理 token,不放散笔。OpenClaw 读取环境变量的路径是固定的,把OPENCLAW_GATEWAY_TOKEN、MODEL_API_KEY这些统一写进项目的.env,启动脚本里一律用set -a; source .env; set +a加载,能少踩很多“为什么我 export 了还是不行”的坑。
第二个习惯是定期轮换 token。我每个月一号会重新生成 gateway token,同时把旧的从配置里删掉。这么做一方面符合安全习惯,另一方面也能避免 token 过期时间不明确导致的尴尬——很多 token 生成时默认 30 天有效,你如果忘了,等到月末正好开始报 1008。
第三个习惯是遇到连接类报错,第一件事开 debug 日志。OPENCLAW_LOG_LEVEL=debug跑一条轻量任务,比瞎猜配置快得多。日志会明确告诉你连接到达了哪一层:是没连上网关,还是网关认证失败,还是模型调用超时。定位到层,问题基本解决一半。
第四个习惯是随时准备一个openclaw doctor命令做环境体检。很多版本都内置了类似命令,能一次性检查 WSL2 状态、网关地址、token 配置和模型服务可达性。虽然不能解决所有问题,但至少能帮你把最蠢的配置错误提前暴露出来。
我这台机器上第一次报 1008 到修好,前后花了不到一小时,其中大半时间花在一个引号上。回过头看,如果一开始就直接看环境变量的真实值,可能五分钟就结束了。希望这篇能帮你绕开我踩过的坑,看到disconnected (1008)的时候,先看一眼日志,再低头检查.env。很多时候,问题不在网络,也不在模型,就在那一行看起来人畜无害的配置里。