NO_PROXY 漏了 0.0.0.0:opencode 只在一个 tmux 窗口启动失败的真凶
十月 09, 2026 [AI] #opencode #proxy #tmux #调试现象
tmux index7 窗口里 opencode -c 启动失败:
Starting background server...
Error: Server process exited with code 1
Error: Managed service port 4096 on 0.0.0.0 is already in use by another process.
其他窗口全部正常。daemon 明明活着(/api/info 1ms 应答 200),客户端却判定服务不存在,
spawn 新进程撞端口。
排查:三层反转
第一层:daemon 没病。 进程活着、监听 4096、探测秒回;版本一致排除 replace 链。 日志里确实有 219 条 >2s 的慢请求(多会话并发),最大 8.1s——差点当成"高负载踩穿探测超时", 但它解释不了"只有 index7 失败"的确定性。
第二层:环境对比扑空。 diff 两个窗口 pane 的 /proc/<pid>/environ,只剩
DISPLAY/SSH_CONNECTION 这类 SSH 会话的正常差异。
第三层:换观测点,实锤。 关键动作是直接在 index7 里跑诊断:
$ opencode service status
stopped ← 同一个 daemon,这个窗口认为服务不存在!
而 /proc/pid/environ 是进程启动时的快照,shell 事后 export 的变量根本不在里面。
直接问 shell 本体:
$ echo ${http_proxy:-none}
http://192.168.5.244:10808 ← 这个窗口在调试 vless 代理
$ echo $NO_PROXY
localhost,127.0.0.1,::1,127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10
NO_PROXY 覆盖了 localhost 和一整圈内网段,唯独没有 0.0.0.0——而 daemon 注册文件里
的 url 恰恰是 http://0.0.0.0:4096(监听地址配了 0.0.0.0 以便 LAN 访问)。
因果链
probe fetch http://0.0.0.0:4096/api/info
→ Bun fetch 尊重 http_proxy,目标主机 "0.0.0.0" 不匹配 NO_PROXY 任何一项
→ 请求丢给 192.168.5.244:10808 的代理入口 → 代理连不了 "0.0.0.0" → 失败
→ ensure 判定服务 missing(daemon 活得好好的)
→ spawn contender → 绑 4096 → EADDRINUSE(撞上健康 daemon)
→ 15s 在位者等待,探测同样走代理失败 → 报错退出
修复验证一行搞定:
$ export no_proxy="0.0.0.0,$no_proxy" NO_PROXY="0.0.0.0,$NO_PROXY"
$ opencode service status
http://0.0.0.0:4096 ← stopped → 健康
教训
/proc/pid/environ是启动快照——排查"同机不同 shell 行为不一致",env diff 之外必须 问 shell 本体要当前值。这次差点被它带进死胡同。- NO_PROXY 是精确字符串匹配,不是网段直觉——写了一堆 CIDR,偏偏漏了
0.0.0.0这个特殊地址。代理变量一 export,所有尊重它的 HTTP 客户端(Bun fetch、curl) 都会中招。 - opencode 的一个小坑:服务注册 url 用的是绑定地址
0.0.0.0,客户端探测时原样 fetch。归一化成127.0.0.1能让所有"localhost 直连"的默认 NO_PROXY 天然免疫—— 值得报个 issue。 - 排查顺序的教训:先验证"故障是否可复现、是否确定性",再找机制。日志里的 8s 慢请求 是真实存在的干扰项,但"只在一个窗口失败"这个确定性特征从一开始就指向了本地环境。