Skip to content

[Bug]: 1.5.0 装上 AppServerClient 原型补丁后消息发不出去(一个会话只能成功 1 条;退回 1.3.0 立即恢复) #2402

Open

Description

@huangdouding

[Bug]: 1.5.0 装上 AppServerClient 原型补丁后消息发不出去(一个会话只能成功 1 条;退回 1.3.0 立即恢复)

影响范围

发送消息 / 会话(Windows 桌面端)

环境

项 值
Codex++ 1.5.0(2026-10-05 22:25:48 由 1.3.0 升级而来;退回 1.3.0 后问题消失)
Codex 桌面端 OpenAI.Codex 26.908.4834.0(owl-app.ini → AppVersion 26.908.40834,UA Chrome/152.0.0.0)
系统 Windows 11 25H2(Build 26200.9550;该注册表项 ProductName 仍显示 "Windows 10 Home China")
启动方式 launchMode: "patch",从 Codex++.lnk 启动
相关设置 codexAppModelWhitelistUnlock = true、relayProfilesEnabled = true、活动档 relayMode = "pureApi"、codexAppServiceTierControls = false
认证 auth.json → auth_mode = "apikey"(走本地中转代理 http://127.0.0.1:4000),未登录 ChatGPT 账号

注意:本机 Codex 应用是 26.908.x(Chrome/152),和 #2393 / #2399 里的 26.930.x(Chrome/154)不是同一个版本。在 26.908 上,app_server_client_capture 是成功的,所以这应该是另一个独立缺陷。

当前行为

1.5.0 下,每个会话只有第一条消息能发出去:

  1. 打开一个已有会话,发第 1 条 → 正常,模型有回复;
  2. 回复结束后再发第 2 条 → 发不出去。输入框右侧发送按钮一直保持浅色(禁用态),点发送 / 回车都没有反应,模型名旁边的小转圈一直在转(界面认为"还在生成中");
  3. 换个会话也一样:每个会话只能成功 1 次;
  4. 重启应用无效 —— 1.5.0 生效期间 Codex++ 主动重启了应用 3 次(22:26:21、22:30:39、22:41:54),问题依旧。

不是网络/模型/供应商的问题:同一时段远端能正常返回,本地代理日志显示最后一次成功响应在 22:34:14,app-server 侧能正常收到 turn/completed。

预期行为

同一会话可以连续发送多条消息。

复现步骤

  1. Windows 装 Codex++ 1.5.0,用 Codex++.lnk 启动应用;
  2. 打开任意已有会话,发 1 条消息,等回答结束;
  3. 再发第 2 条 —— 发不出去。

关键数据:同机同日 A/B(只改 Codex++)

数据来源 %USERPROFILE%\.codex\thread_history_1.sqlite → thread_turns 表:

阶段 时段 成功提交的 turn 数
Codex++ 1.3.0 22:00:00 – 22:25:48(26 分钟) 12
Codex++ 1.5.0(升级后) 22:25:48 – 22:49:46(24 分钟) 1(22:31:52→22:34:20,此后 15 分钟再无任何 turn)
完全退出 Codex++ 后直接启动 Codex 22:49:46 – 22:56:02 1,而且是同一个会话 01a10c79-d54a-7591-a323-7e445c5eea39 的第 2 条:22:50:05→22:50:09,4.6 s 成功
退回 1.3.0 22:56:02 – 22:58:50 2(同一会话连续两条:22:56:32→22:57:37、22:58:01→22:58:12)

第三行是最关键的一步:同一个会话、同一个应用版本、同一份 config.toml、同一个 API Key,只是没有 Codex++ 的注入,第二条消息立刻就发出去了。

22:41:55  被 Codex++ 1.5.0 启动的实例 ── 期间发不出消息
22:49:38  该实例关闭
22:49:46  重新启动(Codex++ 日志中没有对应的 launch / script_loaded / bridge 记录 ⇒ 未经 Codex++ 注入)
22:50:05  turn/started   ← 同一会话的第 2 条消息
22:50:09  turn/completed(4.6 秒)
22:56:02  Codex++ 1.3.0 装回并启动
22:56:32  turn/completed
22:58:12  turn/completed  ← 同一会话连续两条都成功

日志片段

A. 注入过程 —— 抓取成功、补丁装上(%USERPROFILE%\.codex-session-delete\codex-plus.log)

{"event":"manager.start","detail":{"version":"1.5.0"}}
{"event":"renderer.app_server_client_capture_located","detail":{"lineNumber":1796,"columnNumber":87512}}
{"event":"bridge.app_server_client_capture_armed","detail":{"lineNumber":1796,"columnNumber":87512}}
{"event":"bridge.app_server_client_captured","detail":{}}
{"event":"renderer.app_server_client_prototype_patch_installed","detail":{}}

即:没有出现 capture_locate_failed / anchor_missing,和 #2393 不是同一个点;补丁是"成功安装"的。

B. 同期的两个补丁失败

{"event":"renderer.service_tier_dispatcher_patch_failed",
 "detail":{"errorName":"Error","errorMessage":"dispatcher is a non-writable RPC stub (app-initial-)"}}

{"event":"renderer.model_app_server_request_patch_not_found",
 "detail":{"moduleCount":10,"candidateCount":2352,
           "sources":["app://-/assets/app-main-328bbc8378ff.js",
                      "app://-/assets/local-conversation-sources-signals-3065932980c5.js",
                      "app://-/assets/electron-page-host-70c9f6a246c5.js"]}}

C. 健康检查失败 + 一次 panic

{"event":"bridge.reinject_start",
 "detail":{"debug_port":9229,"helper_port":57321,
           "browser_identity_changed":false,"consecutive_health_failures":2}}

{"event":"manager.panic",
 "detail":{"payload":"cannot move state from Destroyed",
           "location":{"file":"...\\tao-0.35.2\\src\\platform_impl\\windows\\event_loop\\runner.rs",
                       "line":371}}}

D. 应用侧:turn/completed 在 22:34:20 之后再没出现,直到 22:50:05

相关代码(codex-plus-plus.exe 1.5.0,20,360,192 字节,注入的 JS 字符串)

const wanted = codexPlusModelUnlockEnabled()                       // ← codexAppModelWhitelistUnlock
  || (codexPlusBackendSettingsLoaded && codexRemoteSessionProviderPatchEnabled())  // ← 中转配置
  || codexPlusSettings().serviceTierControls;
if (!wanted) return false;
...
proto.sendRequest = async function codexPlusModelRequestPatch(method, params, options) {
  ...
  } else if (codexRemoteSessionProviderRequestMethod(requestMethod)
      && codexRemoteSessionProviderOverrideEnabled()
      && !codexRemoteSessionTargetProvider()) {
    await loadCodexModelCatalog();                       // ← POST 127.0.0.1:57321/codex-model-catalog
  }
  const providerParams = applyCodexRemoteSessionProviderOverride(requestMethod, params);
  const nextParams = applyCodexServiceTierRequestOnly(requestMethod, providerParams);
  const modelContextRefresh = await refreshCodexThreadModelBeforeTurn(   // ← 转发前又插了一次 await
    client, originalSendRequest.bind(client), method, nextParams, options);
  const result = await originalSendRequest.call(client, method, nextParams, options);  // ← 真正转发

因为本机 codexAppModelWhitelistUnlock = true 且活动档 relayMode = "pureApi",wanted 必为 true ⇒ 这个 sendRequest 包装器必定安装,也就是所有 RPC(包含 turn/start)都要先过它。

推测(供作者参考)

  1. 包装器在转发前引入了两次 await(loadCodexModelCatalog()、refreshCodexThreadModelBeforeTurn(...))。若其中任一步在"已有会话再次发送"这条路径上永久 pending 或产生自锁,sendRequest 就永远不会 resolve,turn/start 也就永远不会到达 app-server —— 外在表现正好是"按钮点了没反应、界面一直显示生成中"。
  2. 之所以"第一条能发、第二条不行",猜测与 codexPlusBackendSettings / 模型目录的冷热状态有关:第一次走的是另一条分支。
  3. service_tier_dispatcher_patch_failed(non-writable RPC stub)与 model_app_server_request_patch_not_found(moduleCount:10、candidateCount:2352)说明 1.5.0 的补丁锚点在 26.908 上也有偏差,建议和上面一并排查。
  4. 建议:把 sendRequest 包装器的 await 前置逻辑做成"失败即降级为直接转发"(try/catch + 超时),任何注入侧异常都不应阻断用户消息的投递。

已排除

  • 不是会话数据损坏:没装 Codex++ 时在同一个会话里发第二条完全正常。
  • 不是认证 / 中转:auth_mode = apikey,本地代理 22:34:14 仍在正常返回 200。
  • 不是应用版本变化:应用一直是 26.908.4834.0,1.3.0 下 12 个 turn 全部成功。
  • 不是网络:全程本地回环。
  • 不是"卡顿":1.5.0 期间连续 15 分钟、多次重启都无法发送。

临时规避

  1. 退回 Codex++ 1.3.0 —— 本机实测有效(覆盖安装后连续两条同会话消息均正常)。
  2. 或者在设置里把 模型白名单解锁 与 中转配置(relayProfiles) 都关掉,使上面 wanted 的三个条件全部为 false,原型补丁就不会安装(未实测,请作者确认可行性)。
  3. 急用时:完全退出 Codex++,用 explorer.exe shell:AppsFolder\<PackageFamilyName>!App 直接启动 Codex,发送正常(代价是失去皮肤 / 中文补丁 / 各项解锁)。

关联

提交前确认

  • 我已经脱敏了 API Key、Token、账号等敏感信息。
  • 我已经确认这是当前最新版(1.5.0)仍然存在的问题。
  • 我已按"退回 1.3.0"复测,问题消失。

Activity

  1. BigPizzaV3 commented on Oct 8, 2026

    @BigPizzaV3
    Owner

    已直接合入 main 的 edff151 修掉两个确定的阻塞风险:发送前 helper 配置/目录只读等待最多 2 秒,迟到结果不应用;普通请求完成后不再为模型目录迟迟不返回而继续挂起。语音测试对函数签名的提取也已同步修正并纳入 Cargo 回归。

    thread/resume 是会修改会话的 RPC,仍保持严格顺序,没有用超时后继续发送的方式制造迟到写入竞争。这些改动还不能证明你原始“第二条消息发不出”已完全解决,所以本条保留开放。请在包含修复的版本上,用同一 26.908 客户端和同一会话复测,记录第二次 turn/start 是否实际到达 app-server,以及 thread/resume 的完成情况。

  2. huangdouding commented on Oct 9, 2026

    @huangdouding
    Author

    复测结果(Codex++ 1.7.1 + Codex Desktop 26.1002.7124.0):第二条消息未复现

    环境(必须先说明的一点)

    本机客户端在等待期间自动升级了,无法满足"同一 26.908 客户端":

    项 10-05 报告时 本次复测
    Codex++ 1.5.0 1.7.1(含 edff1519)
    Codex Desktop OpenAI.Codex 26.908.4834.0 (26.908.40834) OpenAI.Codex 26.1002.7124.0,app_server.client_version="26.1002.52244",UA Chrome/155.0.8059.27
    注入锚点 app-initial-… app-shared-40678a67f0e3.js(capture_located {lineNumber:487, columnNumber:156892},即 #2399 说的那次漂移后的位置)
    设置 codexAppModelWhitelistUnlock=true、relayProfilesEnabled=true、活动档 relayMode="pureApi" 同

    复测方式:用 CDP(9229)在同一个会话 01a10c79-d54a-7591-a323-7e445c5eea39(就是你上次让我复测时被卡住的那个)里连续发消息,取证全部来自 %USERPROFILE%\.codex\logs_2.sqlite(table logs)与 %USERPROFILE%\.codex\thread_history_1.sqlite(table thread_turns)。

    结果

    1) 第二次 turn/start 确实到达 app-server

    {"ts":"2026-10-10T01:56:05+08:00","target":"codex_app_server::message_processor",
     "body":"app-server request: turn/start connection_id=ConnectionId(0) request_id=String(\"e9080755-7ce5-48b2-aea1-5fadac62b399\")"}
    {"ts":"2026-10-10T01:56:06+08:00","target":"codex_app_server::outgoing_message",
     "body":"app-server event: turn/started targeted_connections=1"}

    对比 1.5.0 时(10-05 22:25:48–22:49:46,24 分钟内 thread_turns 只有 1 条、app-server 完全没有收到 turn/start):该回归不复现。

    2) 同一会话连续多条全部完成(thread_turns,started_at/completed_at 为 epoch 秒)

    rowid thread turn status 时间 duration
    1437 01a10c79… 01a121ce-d658-… completed 01:56:06 → 01:56:10 4468 ms
    1438 01a10c79…(同会话第 2 条) 01a121cf-6681-… completed 01:56:43 → 01:56:44 1160 ms
    1439 01a12179…(另一个老会话) 01a121d0-b37a-… completed 01:58:08 → 01:58:11 2515 ms
    1434 / 1435 新建会话 01a121bf… … completed 01:39:32 → 01:39:34 / 01:41:22 → 01:41:23 2040 / 1339 ms

    3) thread/resume 完成情况正常(桌面端主进程日志)

    info [AppServerConnection] response_routed … method=thread/resume durationMs=845  errorCode=null   conversationId=01a12179…
    info [AppServerConnection] response_routed … method=thread/resume durationMs=1284 errorCode=null   conversationId=01a0de3b…
    

    没有出现你担心的迟到写入竞争或 resume 挂起。

    结论

    #2402 报告的"每个会话只能成功发一条"在 1.7.1 上未复现:turn/start 全部到达 app-server,同一会话可连续发送并正常完成,thread/resume 也正常返回。本条可以按你们的流程关闭;如果你们希望保留直到有 26.908 客户端的复测,请告诉我,我可以尝试固定旧版本再测一轮。

    附:本次复测中撞到、但与本条无关的两个环境问题(供参考)

    1. 线程级 model_provider 被持久化:%USERPROFILE%\.codex\state_5.sqlite → threads.model_provider 计数为 openai=149、OpenAI=1、custom=15、deepseek=1。resume 这些"老会话"时用的是内置 openai provider(ModelProviderInfo { name:"OpenAI", base_url: None, env_http_headers: Some({"OpenAI-Organization":"OPENAI_ORGANIZATION","OpenAI-Project":"OPENAI_PROJECT"}), requires_openai_auth: true, supports_websockets: true }),走 transport="responses_websocket" → wss://api.openai.com/v1/responses 401(auth error code: invalid_api_key),界面停在「正在重新连接 2/5」;而新建会话用配置里的 provider(transport="responses_http" → https://api.deepseek.com/v1/responses 200 OK)。这就是"已经聊过的对话发不出消息"的真实原因 —— 与 1.5.0 的 sendRequest 包装器是两个不同的问题。我把这些线程的 model_provider 改成自定义 provider 后,老会话立刻恢复(上面 1437/1438/1439 就是证据)。
    2. openai_base_url 会导致启动卡死:我试过在 %USERPROFILE%\.codex\config.toml 里加 openai_base_url = "https://api.deepseek.com/v1"(想把内置 openai provider 指到第三方 API,从而不改线程级 provider 就修好老会话)。结果应用卡在启动 splash:渲染层 #root 只渲染了 splash(innerHTML 9748、elCount=34、侧栏元素 0),6 分钟内毫无进展,app-server 侧没有 Configuring session;移除该键后立刻恢复正常(侧栏元素 15、可输入)。怀疑与启动时对上一个线程做 websocket 预连接有关(wss://api.deepseek.com/v1/responses 可能挂住而不是快速失败)。这条我没有做重复实验,仅供参考。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions