{"record":{"id":"55c8a06e69a2d064","repo":"BigPizzaV3/CodexPlusPlus","slug":"app-server","errorCode":null,"errorMessage":"app-server 未连接","messagePattern":"app-server 未连接","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/codex-plus-mobile-relay/src/main.rs","lineNumber":885,"sourceCode":"async function ensureAppServer() {\n  if (appServerConnected && appServerSessionId) return;\n  appServerSessionId = appServerSessionId || `mobile-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n  const id = `${Date.now()}-${requestId++}`;\n  const packet = await encrypt({ type: \"appServerConnect\", id, sessionId: appServerSessionId });\n  socket.send(JSON.stringify(packet));\n  await new Promise((resolve, reject) => {\n    pending.set(id, { resolve, reject });\n    setTimeout(() => { if (pending.delete(id)) reject(new Error(\"app-server 连接超时\")); }, 30000);\n  });\n  await rpcRaw(\"initialize\", { clientInfo: { name: \"Codex++ Mobile Relay\", version: \"1.0.0\" }, capabilities: { experimentalApi: true } });\n}\nasync function rpc(method, params = {}) {\n  await ensureAppServer();\n  return await rpcRaw(method, params);\n}\nasync function rpcRaw(method, params = {}) {\n  if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error(\"未连接\");\n  if (!appServerSessionId) throw new Error(\"app-server 未连接\");\n  const id = rpcId++;\n  const payload = { jsonrpc: \"2.0\", id, method, params };\n  const packet = await encrypt({ type: \"appServerMessage\", sessionId: appServerSessionId, message: JSON.stringify(payload) });\n  const promise = new Promise((resolve, reject) => {\n    pendingRpc.set(String(id), { resolve, reject });\n    setTimeout(() => { if (pendingRpc.delete(String(id))) reject(new Error(`${method} 超时`)); }, method === \"turn/start\" ? 60000 : 30000);\n  });\n  socket.send(JSON.stringify(packet));\n  return promise;\n}\nfunction handleAppServerMessage(text) {\n  let message;\n  try { message = JSON.parse(text); } catch { return; }\n  if (message.id != null) {\n    const resolver = pendingRpc.get(String(message.id));\n    if (!resolver) return;\n    pendingRpc.delete(String(message.id));\n    if (message.error) resolver.reject(new Error(message.error.message || \"请求失败\"));","sourceCodeStart":867,"sourceCodeEnd":903,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/apps/codex-plus-mobile-relay/src/main.rs#L867-L903","documentation":"Raised in mobile_relay_page's ensureAppServer flow: an app-server RPC was issued while appServerConnected is false or no sessionId exists, meaning the appServerConnect handshake never completed (e.g. the 30s connect timeout fired or the relay rejected the connect).","triggerScenarios":"Thrown at apps/codex-plus-mobile-relay/src/main.rs:885 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Retry connect; check app-server startup logs on the relay host","Fix slow app-server startup or timeout handling","Verify the relay can spawn codex app-server at all"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}