Repository navigation
[Bug]: 1.5.0 装上 AppServerClient 原型补丁后消息发不出去(一个会话只能成功 1 条;退回 1.3.0 立即恢复) #2402
Copy link
Copy link
Open
Description
[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 条 → 正常,模型有回复;
- 回复结束后再发第 2 条 → 发不出去。输入框右侧发送按钮一直保持浅色(禁用态),点发送 / 回车都没有反应,模型名旁边的小转圈一直在转(界面认为"还在生成中");
- 换个会话也一样:每个会话只能成功 1 次;
- 重启应用无效 —— 1.5.0 生效期间 Codex++ 主动重启了应用 3 次(22:26:21、22:30:39、22:41:54),问题依旧。
不是网络/模型/供应商的问题:同一时段远端能正常返回,本地代理日志显示最后一次成功响应在 22:34:14,app-server 侧能正常收到 turn/completed。
预期行为
同一会话可以连续发送多条消息。
复现步骤
- Windows 装 Codex++ 1.5.0,用
Codex++.lnk启动应用; - 打开任意已有会话,发 1 条消息,等回答结束;
- 再发第 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)都要先过它。
推测(供作者参考)
- 包装器在转发前引入了两次
await(loadCodexModelCatalog()、refreshCodexThreadModelBeforeTurn(...))。若其中任一步在"已有会话再次发送"这条路径上永久 pending 或产生自锁,sendRequest就永远不会 resolve,turn/start也就永远不会到达 app-server —— 外在表现正好是"按钮点了没反应、界面一直显示生成中"。 - 之所以"第一条能发、第二条不行",猜测与
codexPlusBackendSettings/ 模型目录的冷热状态有关:第一次走的是另一条分支。 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 上也有偏差,建议和上面一并排查。- 建议:把
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 分钟、多次重启都无法发送。
临时规避
- 退回 Codex++ 1.3.0 —— 本机实测有效(覆盖安装后连续两条同会话消息均正常)。
- 或者在设置里把 模型白名单解锁 与 中转配置(relayProfiles) 都关掉,使上面
wanted的三个条件全部为 false,原型补丁就不会安装(未实测,请作者确认可行性)。 - 急用时:完全退出 Codex++,用
explorer.exe shell:AppsFolder\<PackageFamilyName>!App直接启动 Codex,发送正常(代价是失去皮肤 / 中文补丁 / 各项解锁)。
关联
- [Bug]: 无法发送对话 #2393(
无法发送对话):那边是app_server_client_capture_locate_failed / anchor_missing,应用为 26.930.x;本 issue 是抓取成功、补丁装上之后才坏,属于不同的阻断点。 - [Bug]: 1.5.0 在 Codex 26.930.x — AppServerRequestClient 锚点漂移到 app-shared-,模型解锁失效且注入侧空闲 CPU 打满 #2399(1.5.0 锚点漂移到
app-shared-):同一套installCodexAppServerClientCapture()机制,可作为背景参考。
提交前确认
- 我已经脱敏了 API Key、Token、账号等敏感信息。
- 我已经确认这是当前最新版(1.5.0)仍然存在的问题。
- 我已按"退回 1.3.0"复测,问题消失。
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
BigPizzaV3 commentedon Oct 8, 2026
已直接合入 main 的 edff151 修掉两个确定的阻塞风险:发送前 helper 配置/目录只读等待最多 2 秒,迟到结果不应用;普通请求完成后不再为模型目录迟迟不返回而继续挂起。语音测试对函数签名的提取也已同步修正并纳入 Cargo 回归。
thread/resume 是会修改会话的 RPC,仍保持严格顺序,没有用超时后继续发送的方式制造迟到写入竞争。这些改动还不能证明你原始“第二条消息发不出”已完全解决,所以本条保留开放。请在包含修复的版本上,用同一 26.908 客户端和同一会话复测,记录第二次 turn/start 是否实际到达 app-server,以及 thread/resume 的完成情况。
huangdouding commentedon Oct 9, 2026
复测结果(Codex++ 1.7.1 + Codex Desktop 26.1002.7124.0):第二条消息未复现
环境(必须先说明的一点)
本机客户端在等待期间自动升级了,无法满足"同一 26.908 客户端":
edff1519)OpenAI.Codex 26.908.4834.0(26.908.40834)OpenAI.Codex 26.1002.7124.0,app_server.client_version="26.1002.52244",UAChrome/155.0.8059.27app-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(tablelogs)与%USERPROFILE%\.codex\thread_history_1.sqlite(tablethread_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 秒)01a10c79…01a121ce-d658-…01a10c79…(同会话第 2 条)01a121cf-6681-…01a12179…(另一个老会话)01a121d0-b37a-…01a121bf…3)
thread/resume完成情况正常(桌面端主进程日志)没有出现你担心的迟到写入竞争或 resume 挂起。
结论
#2402报告的"每个会话只能成功发一条"在 1.7.1 上未复现:turn/start全部到达 app-server,同一会话可连续发送并正常完成,thread/resume也正常返回。本条可以按你们的流程关闭;如果你们希望保留直到有 26.908 客户端的复测,请告诉我,我可以尝试固定旧版本再测一轮。附:本次复测中撞到、但与本条无关的两个环境问题(供参考)
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/responses401(auth error code: invalid_api_key),界面停在「正在重新连接 2/5」;而新建会话用配置里的 provider(transport="responses_http"→https://api.deepseek.com/v1/responses200 OK)。这就是"已经聊过的对话发不出消息"的真实原因 —— 与 1.5.0 的sendRequest包装器是两个不同的问题。我把这些线程的model_provider改成自定义 provider 后,老会话立刻恢复(上面 1437/1438/1439 就是证据)。openai_base_url会导致启动卡死:我试过在%USERPROFILE%\.codex\config.toml里加openai_base_url = "https://api.deepseek.com/v1"(想把内置 openai provider 指到第三方 API,从而不改线程级 provider 就修好老会话)。结果应用卡在启动 splash:渲染层#root只渲染了 splash(innerHTML9748、elCount=34、侧栏元素 0),6 分钟内毫无进展,app-server 侧没有Configuring session;移除该键后立刻恢复正常(侧栏元素 15、可输入)。怀疑与启动时对上一个线程做 websocket 预连接有关(wss://api.deepseek.com/v1/responses可能挂住而不是快速失败)。这条我没有做重复实验,仅供参考。