Hang / Application hang/ Stall / Execution stall/Unresponsive UI / UI freeze ————always thinking but no response after "git, find,searchAuto-compacting context"

Open 💬 3 comments Opened May 25, 2026 by Kinghammer1
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

Mac_26.519.41501 (3044)

What subscription do you have?

Plus

What platform is your computer?

Mac M4

What issue are you seeing?

Hang / Application hang/ Stall / Execution stall/Unresponsive UI / UI freeze ————always thinking but no response after "git, find,searchAuto-compacting context"。

and i have let codex anaylse the questions:
我查了本地能读到的 rollout 和 sqlite 日志,结论比较明确:卡住的不是 shell 命令,而是工具结果返回之后,模型/会话调度没有继续生成下一步。

时间线是这样:

17:27:30:第二次“分析客观指标”任务开始。
17:27:40:我发了那句“先摸目录结构……”。
17:27:40.676 / 17:27:40.688:发起两个工具调用:pwd 和 rg --files ...。
17:27:40.855:两个工具都返回成功,Wall time: 0.0000 seconds,并且已经列出 3 个文件。
17:27:40.855 之后到 17:34:39:没有新的 assistant 消息、没有新的工具调用、没有 task_complete。
17:34:39:你手动中断。
所以实际卡点是:17:27:40 工具结果已经回来了,但我没有进入“读文件/总结指标”的下一步,也没有给 final。从工具返回到你中断,大约 6 分 59 秒;从任务开始到中断约 7 分 9 秒,体感上就是你说的 8 分钟。

我还查了全局日志:同一时间有一些无关的后台 plugin sync 警告,以及另一个 background thread 的 skill loader warning,但没有看到能证明是 rg、文件扫描、终端命令或项目目录导致的错误。read_thread_terminal 也显示这个线程没有附加终端会话。

所以我的判断是:这是 Codex 会话执行/模型采样流在“工具输出之后”挂住或丢了续跑,不是项目太大、命令卡死,也不是我在扫描。更糟的是后面 17:37 那次也有类似苗头:工具返回后没有继续给完整分析,说明这次线程状态可能当时就有点不稳。
能。按我能看到的本地日志判断:更像是 Codex 本身的会话/工具调用续跑问题,不像是网络问题,也不是你的项目或命令卡住。

关键证据是:

17:27:40 左右,两个本地命令已经成功返回

pwd 立刻返回
rg --files ...L- Object-std 也立刻返回,只列出 3 个文件
工具耗时记录是接近 0 秒
真正的空白发生在工具结果返回之后

17:27:40 工具输出已经写入日志
17:27:40 到 17:34:39 之间没有新的 assistant 输出、没有新的 shell 输出、也没有任务完成记录
直到你手动中断
同一线程后面又出现过类似现象

我调用 read_thread_terminal,它也马上返回了
但之后仍然没有继续生成,直到你再次打断
这说明不是某个 shell 命令卡死,而是“工具返回后,Codex 没有继续接上下一步”
本地网络日志没有看到明显失败

那个时间段附近能看到到 chatgpt.com:443 的连接成功、握手成功
没看到 DNS 失败、TLS 失败、timeout、断连重试这类强网络故障证据
有一个插件 marketplace sync 的警告,但它是后台插件同步,和这次本地项目分析关系不大
所以结论更准确地说是:

不是网络断了,也不是命令跑太久;更可能是 Codex app / 会话调度 / 工具调用后续生成链路在那一轮卡住了。
如果把“Codex 的原因”细分,它不一定是模型“想不出来”,而更像是:工具结果已经回来,但客户端或后端的 turn runner 没有继续触发下一段生成,或者流式响应状态丢了。

What steps can reproduce the bug?

just any prompt all can happen,long long long time thinking but no response

What is the expected behavior?

long time thinking can response

Additional information

_No response_

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #24336
  • #24287
  • #24422
  • #24432

Powered by Codex Action

Kinghammer1 · 3 months ago

@shaofa-ui, @https://github.com/openai/codex/issues/24287,https://github.com/jshaofa-ui。
完整解决方案
根本原因分析
这是Codex Desktop UI(Electron 渲染器)与后端/应用服务器之间的会话/回合状态所有权不同步问题。核心问题:

提示已被接受,但用户界面失去所有权:后端已接受并执行提示,但用户界面的回合订阅未能接收或渲染提示气泡、助手进度和工具轨迹。
停止失败:由于用户界面没有对当前回合的有效引用,因此无法取消停止按钮。
重启后状态不可见:线程的提示/跟踪状态与原始线程没有持久关联,因此重启后显示的历史记录可能具有误导性或缺失。
来自哨兵计划的关键线索:

thread_count_active: 4但是thread_count_streaming_owner: 3——一个活跃线程没有流所有者
thread_count_streaming_without_role: 1— 流线程未分配任何角色
thread_count_streaming_without_active_runtime: 2-3— 没有活动运行时的流线程
Item not found in turn state: 2— 解决状态查找失败问题
修复——三层方法
第 1 层 — 所有权协调(主要修复):

// In app-server's turn management (Rust)
// When a prompt is accepted, ensure the turn is durably linked to its thread
// BEFORE any streaming begins

fn handle_prompt(thread_id: &str, prompt: Prompt) -> Result<TurnId> {
// 1. Create turn record in the thread's durable store FIRST
let turn = Turn::new(thread_id, prompt);
self.store.save_turn(&turn)?; // Durable write before streaming

// 2. THEN start the backend execution
let turn_id = turn.id.clone();
self.backend.execute(turn_id.clone(), prompt);

// 3. Subscribe the UI to the turn stream
self.ui.subscribe(thread_id, turn_id.clone());

Ok(turn_id)
}
第二层——停止/取消可靠性:

// In the Desktop UI's Stop handler
async handleStop() {
// Get the current turn_id from the durable store, not just in-memory state
const turnId = await this.store.getActiveTurnId(this.threadId);
if (!turnId) {
// Show recovery UI instead of silently failing
this.showRecoveryState("No active turn found. Checking backend state...");
const backendTurn = await this.appServer.findActiveTurn(this.threadId);
if (backendTurn) {
// Re-attach UI to the backend turn
await this.reattachToTurn(backendTurn);
}
return;
}
await this.appServer.cancelTurn(turnId);
}
第 3 层 — 恢复/重新连接状态:

// When the UI detects it has lost ownership of an active turn
async detectAndRecover() {
const localActive = await this.store.getActiveTurns(this.threadId);
const backendActive = await this.appServer.getActiveTurns(this.threadId);

// Find turns that exist in backend but not in UI
const orphanedTurns = backendActive.filter(
bt => !localActive.some(lt => lt.id === bt.id)
);

for (const orphaned of orphanedTurns) {
// Show a recovery banner
this.showRecoveryBanner({
type: 'orphaned_turn',
turnId: orphaned.id,
message: 'An active turn was found that is not visible in the UI. Click to re-attach.',
action: () => this.reattachToTurn(orphaned)
});
}
}
测试策略
单元测试:验证在流式传输开始之前,回合数是否已持久存储
集成测试:模拟回合进行中 UI 崩溃,验证重启后是否恢复
特殊情况:验证即使用户界面失去回合所有权,“停止”功能也能正常工作。
回归测试:验证正常的流程(提示→流→响应)是否不受影响
要更改的文件
codex-rs/app-server-daemon/src/turn_manager.rs— 在流媒体播放前添加耐用的旋转存储
app/src/turn-state-manager.ts— 添加恢复/重新连接逻辑
app/src/stop-handler.ts— 使用持久化轮次引用而不是内存中的轮次引用
影响
提示与其起源线程永久关联。
停止按钮能够可靠地取消后端转向或显示恢复状态
孤立回合会被检测到,并且可以在重启后恢复。
没有来自不可见已接受提示的重复提示。

MisterRound · 3 months ago

This issue is becoming widespread, it matches mine https://github.com/openai/codex/issues/24260