1. openclaw 服务连接成功但立即关闭:先分清「连不上」和「连上就断」
openclaw 服务连接成功但立即关闭这个现象,本质上是两个完全不同的问题叠在一起:一个是端口绑定范围不对,导致局域网设备根本连不进来;另一个是服务进程本身在握手后崩溃或主动断开,导致本机 telnet 显示Connected之后马上Connection closed by foreign host。很多人看到「连接成功」四个字就以为网络通了,其实那只是 TCP 三次握手完成,应用层还没开始说话就被对端关掉了。
openclaw 是一套把本地工具、模型网关和自动化任务串起来的服务框架,常见形态是 Docker 容器里跑一个 gateway,对外暴露一个端口(示例里是 18789),内部再通过 HTTP 或 WebSocket 跟模型 endpoint 通信。它适合谁?适合想把本地脚本、编辑器插件、Agent 流程统一到一个入口的开发者,也适合需要在内网多台机器之间共享一个模型通道的团队。它的连接稳定性直接取决于三件事:端口绑定地址、进程存活状态、以及上游 endpoint 的鉴权配置是否有效。
我先把结论摆出来:telnet 127.0.0.1 18789返回Connected然后立刻Connection closed by foreign host,说明服务在监听,但 accept 之后进程退出或主动 close;telnet 192.168.1.24 18789返回Connection refused,说明服务压根没绑定到那个网卡地址。前者要查日志找崩溃原因,后者要改docker-compose.yml的 ports 映射。而当你把 endpoint 换成统一 Key 通道之后,还要再验证一次连接是否稳定,因为鉴权失败同样会让服务在启动后立刻关闭。
下面按「定位端口 → 抓关闭日志 → 改配置 → 换 endpoint → 验证 → 排错」的顺序走一遍,每一步都给可复制的命令和预期输出。你不需要一次全做完,按现象对号入座即可。
2. 端口绑定与连接关闭日志:用 docker logs 和 ss 定位 openclaw 崩溃点
先确认服务到底监听在哪个地址。很多人只看docker ps显示Up就放心了,但容器 Up 不代表端口映射正确。用下面这条命令看真实监听状态:
ss -tlnp | grep 18789预期输出类似:
LISTEN 0 128 127.0.0.1:18789 0.0.0.0:* users:(("docker-proxy",pid=12345,fd=4))如果看到的是127.0.0.1:18789,那局域网设备必然Connection refused,因为 docker-proxy 只绑了回环地址。如果看到0.0.0.0:18789或*:18789,才说明对所有网卡开放。这一步是判断「端口绑定」问题的最快方式,比反复 telnet 高效得多。
接着抓连接关闭的日志。Connection closed by foreign host是客户端视角,服务端视角要看容器日志:
docker logs --tail 100 openclaw-gateway重点找这几类关键字:panic、fatal、exit、auth、unauthorized、connection reset、context canceled。如果日志里出现listen tcp 127.0.0.1:18789: bind: address already in use,那是端口冲突;如果出现invalid api key或401,那是鉴权配置问题,服务在启动握手阶段就被上游拒绝,于是主动关闭了本地连接。
再补一条实时跟踪命令,方便你一边 telnet 一边看日志:
docker logs -f openclaw-gateway然后在另一个终端执行:
telnet 127.0.0.1 18789你会看到日志里同步打印出 accept 和 close 的记录。如果 close 紧跟在 accept 之后,且没有任何业务日志,基本可以判定是启动阶段崩溃,而不是运行中崩溃。这时候要检查配置文件是否完整:
cat ~/openclaw/data/openclaw.json | grep -E "token|endpoint|model"如果endpoint指向的是一个不可达或鉴权失败的地址,服务会在初始化连接池时抛错退出。这也是为什么后面要把 endpoint 换到统一 Key 通道——它把鉴权收敛到一个入口,减少每个服务各自配 Key 导致的失败面。
顺便说一个容易忽略的点:docker-compose.yml里如果写了restart: always,容器崩溃后会立刻重启,你 telnet 的时候可能正好连上重启前的旧进程,然后旧进程退出,新进程还没起来,于是表现为「连上就断」。用docker ps -a看STATUS列有没有频繁的Restarting,能快速识别这种情况。
3. 可复制配置:docker-compose.yml 端口映射与统一 Key 通道 settings 片段
先解决端口绑定。进入 openclaw 目录,编辑 compose 文件:
cd ~/openclaw vim docker-compose.yml如果你只需要本机访问,保持:
ports: - "127.0.0.1:18789:18789"如果局域网设备(比如 192.168.1.24 那台)也要连,改成:
ports: - "0.0.0.0:18789:18789"或者只绑特定网卡:
ports: - "192.168.1.24:18789:18789" - "127.0.0.1:18789:18789"改完重启:
docker compose down docker compose up -d docker ps | grep openclaw接下来是统一 Key 通道的配置。openclaw 的 endpoint 通常写在openclaw.json或环境变量里。把上游地址改成 TaoToken 的 API 入口,Base URL 用https://taotoken.net/api,Key 用你在控制台生成的统一 Key,Model ID 按你实际使用的模型填。一个可复制的 JSON 片段如下(路径与原文一致,放在~/openclaw/data/openclaw.json):
{ "gateway": { "listen": "0.0.0.0:18789", "auth": { "mode": "token", "token": "你的本地访问token" } }, "upstream": { "base_url": "https://taotoken.net/api", "api_key": "你的TaoToken统一Key", "model": "claude-sonnet-4-20250514", "timeout_seconds": 60 } }如果你用的是环境变量方式,等价写法:
export OPENCLAW_UPSTREAM_BASE_URL="https://taotoken.net/api" export OPENCLAW_UPSTREAM_API_KEY="你的TaoToken统一Key" export OPENCLAW_UPSTREAM_MODEL="claude-sonnet-4-20250514"这里三件套必须齐全:Base URL、Key、Model ID。缺任何一个,服务启动时都会在鉴权阶段失败并关闭连接。Key 的获取入口在控制台的 API Keys 页面,文档在接入文档里,两个地址分别是https://taotoken.net/console/api-keys和https://taotoken.net/doc,都带上utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite这类归因参数方便你回查。
改完配置后重启容器,让新 endpoint 生效:
docker compose restart openclaw-gateway docker logs --tail 30 openclaw-gateway如果日志里出现upstream connected或类似的成功标记,说明统一 Key 通道已经接上。如果还是Connection closed,先别急着改端口,去看日志里的鉴权报错,多半是 Key 复制时带了空格或换行。
4. 验证请求:curl 与 telnet 双通道确认 openclaw 连接稳定性
配置改完必须验证,不能只看容器 Up。分三层验证:端口层、HTTP 层、模型层。
端口层用 telnet 或 nc:
telnet 127.0.0.1 18789 telnet 192.168.1.24 18789本机应该保持连接不自动断开(除非你按 Ctrl+] 退出)。如果还是Connection closed by foreign host,回到第 2 步看日志。局域网那条如果从refused变成Connected,说明端口绑定改对了。
HTTP 层用 curl 看返回:
curl -v http://127.0.0.1:18789/health curl -v http://192.168.1.24:18789/health预期返回200加一段 JSON,比如{"status":"ok","upstream":"connected"}。如果返回401,说明本地 token 没带对;如果返回502,说明上游 endpoint 不通,这时候检查https://taotoken.net/api是否可达,以及 Key 是否有效。
模型层直接发一次对话请求,确认统一 Key 通道真的能出结果:
curl -s https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer 你的TaoToken统一Key" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-4-20250514", "messages": [{"role": "user", "content": "ping"}], "max_tokens": 16 }'如果返回里有choices字段和内容,说明 Key 和 endpoint 都正常。这一步很关键,因为 openclaw 服务连接成功但立即关闭,很多时候不是 openclaw 本身的问题,而是上游鉴权失败导致它在启动时退出。把上游单独验证一遍,能直接排除这一层。
验证通过后,再回头看 openclaw 的日志,应该能看到稳定的心跳或请求记录,而不是反复的 accept/close。如果你想让验证更直观,可以在模型对话页面手动发一条消息,观察是否秒回,这比看日志更贴近真实使用体验。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth 逐条对照
报错一:401 Unauthorized。日志里出现401或invalid api key,说明 Key 不对。检查三处:openclaw.json里的api_key、环境变量OPENCLAW_UPSTREAM_API_KEY、以及 curl 测试时用的 Key 是否一致。常见坑是 Key 前后有空格,或者复制时漏了前缀。统一 Key 通道的好处是只需要维护一个 Key,改一处即可,不用每个服务单独配。
报错二:local proxy failed。这个报错通常出现在服务尝试通过本地代理转发请求时。日志里会写local proxy failed: dial tcp ... connection refused。原因是 endpoint 指向了一个本地未启动的代理端口,或者代理配置残留。解决办法是把base_url直接改成https://taotoken.net/api,不要经过任何本地中间层。改完重启容器,日志里应该不再出现 proxy 相关字样。
报错三:reading choices。这个报错一般出现在解析上游响应时,日志类似error reading choices: unexpected end of JSON input。说明上游返回的不是标准 JSON,可能是鉴权失败返回了 HTML 错误页,或者超时被截断。先确认base_url拼写正确,没有多斜杠或少/v1;再确认model字段是上游支持的 Model ID。如果 Model ID 写错,有些网关会返回非 JSON 的错误体,导致解析失败。
报错四:OAuth 相关。如果日志里出现OAuth token expired或refresh token failed,说明你之前用的是 OAuth 方式鉴权,token 过期后服务启动即退出。切换到统一 Key 通道后这类问题会消失,因为 Key 鉴权不依赖刷新流程。切换时记得把旧的 OAuth 配置字段删掉或注释,避免服务同时读两套配置产生冲突。
报错五:端口占用。bind: address already in use说明 18789 被别的进程占了。用ss -tlnp | grep 18789找到占用进程,要么杀掉,要么改 openclaw 的监听端口,同时同步改 compose 映射。
排查顺序建议固定为:先看docker logs定位报错类型,再对照上面五类处理,最后用第 4 步的三层验证确认修复。不要一上来就改端口,很多「连接关闭」其实是鉴权问题伪装出来的。
6. 把 endpoint 收敛到统一 Key 通道:openclaw 长期稳定运行的接入路径
端口绑定解决的是「能不能连进来」,统一 Key 通道解决的是「连进来之后能不能稳定干活」。这两件事分开处理,排查效率会高很多。我试过把多个本地服务的上游都指向同一个入口,最大的好处是鉴权失败只会在一个地方暴露,而不是每个服务各自报不同的错。
如果你只是临时验证模型连通性,用模型对话页面手动发一条消息最快。如果你要长期跑编码任务或 Agent 流程,建议走 Coding Plan,把额度、模型和 Key 统一管理,避免每个容器各自配 Key 导致混乱。接入文档里有完整的 Base URL、Key、Model ID 三件套说明,照着填即可。
最后给一个实用技巧:在docker-compose.yml里加健康检查,让容器在启动失败时自动暴露状态,而不是等你 telnet 才发现:
healthcheck: test: ["CMD", "curl", "-f", "http://127.0.0.1:18789/health"] interval: 10s timeout: 3s retries: 3加完之后docker ps的 STATUS 列会显示healthy或unhealthy,比手动 telnet 直观。配合restart: unless-stopped,服务崩溃后会自动拉起,连接关闭的窗口期也会短很多。到这一步,openclaw 服务连接成功但立即关闭的问题基本就闭环了:端口绑定查ss,崩溃原因查docker logs,鉴权问题查统一 Key 通道,三层验证用 telnet、curl、模型请求各跑一遍。