WSL 下 Open Folder 切换工作区后陷入 Reload Window 重连循环,完全无法使用

Environment

  • Qoder: 1.19.2 (VSCodium 1.106.3, commit 89941185e4)
  • OS: Windows → WSL2 Ubuntu 20.04
  • WSL 配置: memory=6GB, swap=2GB, processors=4
  • 连接方式: Remote-WSL

Reproduction Steps

  1. 通过 Qoder 连接 WSL,打开任意目录(如 /home/qing)→ 一切正常
  2. 使用 File → Open Folder 切换到其他目录(如 /home/qing/go/src/home/qing/go/src/llm-d/llm-d-router
  3. 窗口触发 Reload Window
  4. Reload 后开始频繁 reconnect,每隔几秒循环一次,完全无法使用

补充说明:

  • 第一次连接永远正常,只有 Open Folder 切换后触发
  • 与目标目录文件数量无关(打开 /home/qing 文件更多但正常,打开单个项目 llm-d-router 也会循环)
  • 如果在 Windows 侧直接用新窗口打开 WSL 中的目标目录(不走 Open Folder),则正常

Expected Behavior

Open Folder 切换工作区后,Reload Window 一次即恢复正常连接。

Actual Behavior

Reload Window 陷入无限循环:连接 → 2~14秒后断开 → 重连 → 再断开 → 循环。

Root Cause Analysis

通过分析 ~/.qoder-server/data/logs/ 下频繁重连的日志,发现是 workspace storage 锁竞争 导致的 reload 循环:

循环机制

Open Folder → Reload Window
  → 旧 Extension Host 被 terminate,锁标记 "6000ms 后释放"
  → 新 Extension Host 立即启动(<2s),尝试获取锁
  → EEXIST: vscode.lock 已存在,elapsed < 6000ms,判定非 stale,放弃
  → 使用备用锁 (xxx-1/vscode.lock)
  → 渲染进程检测到异常,再次发送 terminate
  → 循环

关键日志

1. 锁竞争(exthost/remoteexthost.log):

[error] Error: EEXIST: file already exists, open '/home/qing/.qoder-server/data/User/workspaceStorage/32df426cb7af36a347ed6120fb7ec54c/vscode.lock'
[info] Lock '...vscode.lock': Could not acquire lock, checking if the file is stale.
[info] Lock '...vscode.lock': The lock does not look stale, elapsed: 50728 ms, giving up.
[info] Lock '...32df426cb7af36a347ed6120fb7ec54c-1/vscode.lock': Lock acquired.

2. 渲染进程主动 terminate(exthost/remoteexthost.log):

[info] Extension host terminating: received terminate message from renderer

3. 客户端 graceful disconnect(remoteagent.log):

[info] The client has disconnected gracefully, so the connection will be disposed.
[info] Extension Host Process exited with code: 0, signal: null.
[info] Last EH closed, waiting before shutting down

4. 新 server 启动后收到旧 token(remoteagent.log):

[error] [ManagementConnection] Unknown reconnection token (never seen).
[error] [ExtensionHostConnection] Unknown reconnection token (never seen).

5. 重连频率(日志目录时间戳):

20260729T092222
20260729T092245  (+23s)
20260729T092859
20260729T092934  (+35s)
20260729T093010  (+36s)
20260729T093042  (+32s)
20260729T093119  (+37s)
20260729T093224  (+65s)
20260729T093328  (+64s)

11 分钟内产生了 9 次 server 重启。

核心问题

锁释放有 6000ms 延迟(Marking the lockfile as scheduled to be released in 6000 ms),但渲染进程在 ~2s 后就发起了重连。新 Extension Host 拿不到主锁,渲染进程认为工作区状态异常,再次触发 reload,形成死循环。

在本地文件系统上这个时间窗口可能足够小不会触发,但 WSL 的 9P 文件系统放大了延迟,使竞态条件必现。

Suggested Fix

  1. 客户端在 reload 前应等待旧 Extension Host 完全退出并释放锁,而不是固定时间后直接重连
  2. 新 Extension Host 获取锁失败时,应重试等待而非立即使用备用锁,避免渲染进程检测到不一致状态
  3. 或者:Open Folder 在 WSL Remote 下应复用当前 server 进程,仅切换 workspace,而非完整的 terminate → restart 流程

Workaround

  • 不使用 Open Folder 切换目录,改为在 Windows 侧用新窗口直接打开 WSL 目标路径
  • 或手动删除 ~/.qoder-server/data/User/workspaceStorage/*/vscode.lock 后再 Open Folder(可打断循环但不根治)

我也遇到了remote-wsl反复断联的问题,但是可能和你的不一样,是另一个bug导致,就是说他们的remote还有其他问题

Qoder Remote WSL 断联问题分析报告

分析日期:2026-08-03
环境:Windows 25H2 + WSL2 Ubuntu-26.04 + Qoder 1.21.2
Server commit: 7fbe5a663d90672b3e3ceb02e3391a876bc52709


1. 问题现象

Qoder 通过 WSL Remote 连接 Ubuntu-26.04 时,反复出现断联,具体表现为:

  • Server 端日志持续输出 Got delay-shutdown request while in shutdown timeout, delaying
  • 客户端报错 Unknown reconnection token (never seen)
  • 每隔约 60 秒触发一次 delay-shutdown 循环
  • 即使初始连接成功建立,后续仍会断联

2. 环境信息

项目
WSL 内存 48 GB(空闲 46 GB)
CPU 核心数 32 核
系统负载 0.00(几乎为零)
磁盘空间 951 GB 可用
OOM Kill 记录
网络连通性 Test-NetConnection 127.0.0.1:39237TcpTestSucceeded: True

结论:WSL 环境资源充裕,排除性能瓶颈。


3. 关键日志分析

3.1 Server 端日志 (~/.qoder-server/.7fbe5a663d90672b3e3ceb02e3391a876bc52709.log)

Server bound to 127.0.0.1:46063 (IPv4)
Extension host agent listening on 46063

[22:39:07] Extension host agent started.
[22:39:07] [127.0.0.1][ea7b1dc3][ManagementConnection] Unknown reconnection token (never seen).
[22:39:07] [127.0.0.1][feb9b456][ExtensionHostConnection] Unknown reconnection token (never seen).

Server 刚启动即收到带有旧 token 的连接请求,被拒绝。

3.2 客户端 renderer.log 完整时间线

时间 事件 耗时/备注
01:15:53 开始 resolveAuthority -
01:16:33 Server 解析完成,端口 3401 等待 40 秒(server 下载/启动)
01:16:43 Management 连接建立 10 秒
01:16:53 ExtensionHost 连接建立 20 秒
01:17:33 Socket 超时(23 条未确认消息,20 秒无数据) 连接中断
01:17:33 重连到端口 35379(新 server 实例) 重连成功
01:18:05 新连接建立(Management 4ms,ExtHost 48ms) 连接正常
01:19:05 Remote extension host 60 秒未发送 ready 消息 核心错误
01:20:49 窗口重载 -
01:20:50 重连尝试 #1 → 旧 server 仍在运行,端口 35379 使用旧 token
01:21:33 重连尝试 #2 → server 已死,新 server 启动,端口 46063 新 token 生成
01:21:34 客户端发送旧 reconnection token → 被拒绝 Unknown reconnection token
01:21:34 客户端报告 A permanent error occurred 连接彻底失败

3.3 安装脚本日志对比(关键证据)

Attempt #1(01:20:50)— server 还在运行:

Server script is already running    ← 旧 server 存活
listeningOn==35379==
connectionToken==45e67c53-95ef-41a6-b12d-cc9958afd9ab==

Attempt #2(01:21:33)— 43 秒后,server 已死:

Server script already installed     ← 注意:没有 "is already running"
                                    ← 脚本检测到 server 死亡
                                    ← 删除旧 token,启动新 server
listeningOn==46063==                ← 新端口
connectionToken==734d1ecc-938d-4c76-b652-98ba957013c8==  ← 新 token

但客户端的行为:

5/6. sending ConnectionTypeRequest control message.
received error: Connection error: Unknown reconnection token (never seen)

客户端拿到新 server 地址后,没有使用新的 connectionToken 做全新连接,而是使用旧 server 的 reconnection token 尝试"重连"。


4. 根本原因

死循环链路

┌───────────────────────────────────────────────────────────────┐
│                                                               │
│  ① Server 启动(新端口、新 token)                              │
│      ↓                                                        │
│  ② 客户端调用 resolveAuthority 获取新 server 地址               │
│      ↓                                                        │
│  ③ 客户端使用【旧 reconnection token】发起连接(而非新 token)    │
│      ↓                                                        │
│  ④ 新 server 拒绝:Unknown reconnection token                  │
│      ↓                                                        │
│  ⑤ 连接失败,客户端报告 permanent error                         │
│      ↓                                                        │
│  ⑥ Server 无活跃连接 → auto-shutdown 触发                      │
│      ↓                                                        │
│  ⑦ Server 死亡 → 回到 ①                                       │
│                                                               │
└───────────────────────────────────────────────────────────────┘

核心问题

open-remote-wsl 扩展在 server 重启后,没有正确使用新的 connectionToken 建立全新连接,而是错误地走了"重连"路径,使用已失效的 reconnection token。

这是 WebSocket 连接协议层面的 bug:

  • 新连接流程:使用 connectionToken(安装脚本返回的)→ 服务端签发 reconnection token → 后续重连使用此 token
  • 重连流程:使用之前签发的 reconnection token → 仅在同一个 server 实例内有效
  • Bug 表现:server 重启后(新实例),客户端仍尝试用旧 server 签发的 reconnection token 连接