问题日期: 2025-10-11 修复版本: 1.0.174 (待发布)
用户在使用 --cloud-mode 启动时遇到致命错误:
❌ 启动云端模式失败: TypeError: Cannot read properties of undefined (reading 'OPEN')
at Pz.isConnected (file:///opt/homebrew/lib/node_modules/deepv-code/bundle/dvcode.js:3049:7362)
错误发生在MCP服务器(特别是chrome-devtools)尝试连接时,导致云端模式完全无法启动。
正常交互模式和云端模式在MCP初始化上存在关键差异:
gemini.tsx:main()
↓
config.initialize()
↓
创建ToolRegistry
↓
setTimeout(300ms) → discoverMcpToolsAsync() ← 延迟为UI优化
↓
渲染UI (AppWrapper)
↓
300ms后 → MCP开始连接
✅ 成功原因: 300ms延迟让UI完全渲染,避免MCP进程启动阻塞输入响应
gemini.tsx:main() → startCloudMode()
↓
remoteServer.startCloudMode()
↓
remoteSession.initialize()
↓
config.initialize()
↓
setTimeout(300ms) → discoverMcpToolsAsync() ← 同样的延迟
↓
立即创建GeminiChat ← ⚠️ 没有等待MCP
↓
建立WebSocket连接 ← ⚠️ 可能在MCP连接前
↓
MCP尝试使用未初始化的WebSocket → ❌ CRASH
❌ 失败原因: 云端模式没有UI渲染,300ms延迟成为竞态条件触发点
-
300ms延迟的设计意图(来自
config.ts注释):// 延迟300ms确保UI完全渲染和响应用户输入后再启动MCP服务器进程 // MCP进程启动会占用事件循环,即使是异步的也会影响输入响应
-
WebSocket未初始化问题:
- MCP客户端(特别是StdioClientTransport)在连接过程中可能检查WebSocket状态
- 当
this.ws为null或undefined时,访问ws.readyState会抛出TypeError - 错误信息虽然显示"reading 'OPEN'",但实际是WebSocket实例本身未定义
-
NULL Safety问题:
cloudClient.ts的isClosed()方法混合使用了null检查和直接访问remoteSession.ts的sendMessage()没有optional chaining保护
文件: packages/cli/src/remote/remoteSession.ts
添加 waitForMcpDiscovery() 方法,在session初始化时显式等待MCP工具发现完成:
async initialize(): Promise<void> {
await this.config.initialize();
// ⭐ 等待MCP discovery完成(云端模式关键修复)
await this.waitForMcpDiscovery();
// ... 继续初始化
}
private async waitForMcpDiscovery(): Promise<void> {
const timeout = 15000; // 15秒超时
const checkInterval = 100; // 每100ms检查一次
// 获取配置的MCP服务器
const mcpServers = this.config.getMcpServers() || {};
const serverNames = Object.keys(mcpServers);
if (serverNames.length === 0) {
return; // 无MCP服务器,直接跳过
}
// 等待discovery完成
while (Date.now() - startTime < timeout) {
const discoveryState = getMCPDiscoveryState();
if (discoveryState === MCPDiscoveryState.COMPLETED) {
// 记录连接状态(成功/失败都继续)
const connectedServers = serverNames.filter(name =>
getMCPServerStatus(name) === MCPServerStatus.CONNECTED
);
console.log(`✅ MCP discovery完成: ${connectedServers.length}/${serverNames.length} 服务器已连接`);
return;
}
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
// 超时警告但不抛异常 - 允许继续运行
console.warn(`⚠️ MCP discovery超时,继续启动(MCP工具可能不可用)`);
}文件: packages/cli/src/remote/cloudClient.ts
// 修复前
private isClosed(): boolean {
return !this.ws ||
this.ws.readyState === WebSocket.CLOSED || // ❌ 无null检查
this.ws.readyState === WebSocket.CLOSING;
}
// 修复后
private isClosed(): boolean {
return !this.ws ||
this.ws?.readyState === WebSocket.CLOSED || // ✅ Optional chaining
this.ws?.readyState === WebSocket.CLOSING;
}// cleanup() 方法
if (this.ws) {
this.ws.removeAllListeners();
if (this.ws?.readyState === WebSocket.OPEN || // ✅ Optional chaining
this.ws?.readyState === WebSocket.CONNECTING) {
this.ws.close();
}
this.ws = null;
}文件: packages/cli/src/remote/remoteSession.ts
// 修复前
if (this.ws.readyState === WebSocket.OPEN) { // ❌ 无null检查
this.ws.send(JSON.stringify(messageWithSession));
}
// 修复后
if (this.ws?.readyState === WebSocket.OPEN) { // ✅ Optional chaining
this.ws.send(JSON.stringify(messageWithSession));
} else {
remoteLogger.warn('WebSocket未连接', {
readyState: this.ws?.readyState ?? 'null' // ✅ Null-safe logging
});
}关键设计原则: MCP连接失败不应中断主业务
- ✅
waitForMcpDiscovery()超时后继续运行,只记录警告 - ✅ 记录每个MCP服务器的连接状态(成功/失败)
- ✅ 允许部分MCP服务器失败,只要至少有一个成功即可
- ✅ 所有MCP失败也能正常启动,只是工具集受限
npm run build
# ✅ 构建成功,无TypeScript错误dvcode --cloud-mode
🔄 Authentication attempt 1/3...
✅ Authentication successful!
✅ [Cloud Mode] 已认证用户: xxx
🔄 [RemoteSession] 等待MCP discovery完成,已配置 1 个服务器: chrome-devtools
✅ [RemoteSession] MCP discovery完成: 1/1 服务器已连接
已连接: chrome-devtools
✅ [RemoteSession] 会话初始化完成
🆔 CLI ID: cli_xxx
✅ Connected to cloud server
dvcode --cloud-mode
🔄 Authentication attempt 1/3...
✅ Authentication successful!
🔄 [RemoteSession] 等待MCP discovery完成,已配置 1 个服务器: chrome-devtools
Error connecting to MCP server 'chrome-devtools': Connection failed
✅ [RemoteSession] MCP discovery完成: 0/1 服务器已连接
⚠️ 连接失败(已忽略): chrome-devtools
✅ [RemoteSession] 会话初始化完成 ← ⚠️ 关键:继续启动!
✅ Connected to cloud server
dvcode --cloud-mode
✅ Authentication successful!
🔄 [RemoteSession] 等待MCP discovery完成...
⚠️ [RemoteSession] MCP discovery超时(15000ms),继续启动会话(MCP工具可能不可用)
✅ [RemoteSession] 会话初始化完成
✅ Connected to cloud server
-
packages/cli/src/remote/remoteSession.ts- 添加
waitForMcpDiscovery()方法 - 修复
sendMessage()的null safety - 导入MCP状态追踪函数
- 添加
-
packages/cli/src/remote/cloudClient.ts- 修复
isClosed()的null safety - 修复
cleanup()的null safety
- 修复
- ✅ 正常交互模式(gemini.tsx):仍使用300ms延迟,保持UI优化
- ✅ 非交互模式(nonInteractiveCli.ts):可能受益于同样的修复
- ✅ VSCode扩展:不使用云端模式,不受影响
- ✅ Core包:MCP客户端逻辑未改变
- 添加集成测试: 测试云端模式下MCP连接的各种场景
- 性能监控: 记录MCP discovery实际耗时
- 超时配置化: 将15秒超时改为可配置参数
- 统一初始化流程: 考虑将MCP discovery逻辑移到
config.initialize()内部 - 环境变量检测: 检测是否在云端模式,跳过300ms延迟
- Connection Pool: 实现WebSocket连接池,避免重复初始化
- 用户报告: 云端模式启动失败
Cannot read properties of undefined (reading 'OPEN') - 根因: MCP初始化与WebSocket连接的竞态条件
- 解决: 显式等待MCP discovery完成,添加容错处理
用户在升级后可以通过以下方式验证修复:
# 1. 测试云端模式启动
dvcode --cloud-mode
# 2. 观察日志输出
# 应该看到 "✅ [RemoteSession] MCP discovery完成" 而不是crash
# 3. 测试MCP工具可用性
# 在云端会话中使用MCP工具(如chrome-devtools)
# 4. 测试MCP失败容错
# 临时配置一个无效的MCP服务器,验证不会阻断启动本次修复解决了云端模式下的致命启动错误,关键改进包括:
- ✅ 显式等待机制: 确保MCP discovery在WebSocket连接前完成
- ✅ 容错设计: MCP失败不中断主业务流程
- ✅ NULL Safety: 所有WebSocket访问都添加optional chaining保护
- ✅ 详细日志: 记录MCP连接状态,便于问题诊断
修复后,云端模式将能够稳定启动,即使MCP服务器连接失败也能正常运行。