Shell tool appears stuck when a completed git push output is not surfaced promptly
What version of Codex CLI is running?
v0.130.0
What subscription do you have?
pro 20x
Which model were you using?
gpt-5.5
What platform is your computer?
Darwin 25.4.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
iterm2
Codex doctor report
What issue are you seeing?
Summary
In Codex CLI, a git push command appeared to hang for several minutes even though the command eventually completed successfully. The UI showed the command as still running, but
when the session was polled later, the push output appeared immediately and included the successful remote branch creation message.
This made it look like git push or approval handling was stuck, while the underlying process may already have completed or was waiting without visible progress.
Environment
- OS: macOS
- Shell: zsh
- Repo command:
git push -u origin fix/video-color-metadata - Network target: GitHub over HTTPS
What Happened
- Codex ran:
```bash
git push -u origin fix/video-color-metadata
- The tool returned a running session instead of final output.
- The UI appeared stuck for several minutes.
- After polling the session, the command exited successfully and printed:
remote:
remote: Create a pull request for 'fix/video-color-metadata' on GitHub by visiting:
remote: https://github.com/.../pull/new/fix/video-color-metadata
remote:
To https://github.com/...
- [new branch] fix/video-color-metadata -> fix/video-color-metadata
branch 'fix/video-color-metadata' set up to track 'origin/fix/video-color-metadata'.
Expected Behavior
If the process is still running, Codex should surface that clearly and continue streaming/polling progress where possible.
If the process has already completed, the final output should be surfaced promptly without requiring a delayed manual poll.
Actual Behavior
The command appeared stuck for several minutes, causing confusion about whether:
- the shell command was still running,
- GitHub authentication/network was blocked,
- the approval system was waiting,
- or the tool session had completed but output was not displayed.
Why This Matters
For commands like git push, git fetch, or git commit, delayed output can make users think the agent is stuck or doing unintended work. This is especially confusing when previous
commands also involved approval prompts.
Suggested Improvement
- Show a heartbeat/status message for long-running shell sessions.
- Automatically poll running shell sessions more frequently after commands that usually complete quickly, such as git push.
- Distinguish clearly between:
- approval review pending,
- process still running,
- process completed but output not yet surfaced.
- Optionally warn after N seconds with: “Command is still running; last output was X seconds ago.”
What steps can reproduce the bug?
use the auto-mode
What is the expected behavior?
should have take like 10 secs, but it took 4+minutes for the approval
Additional information
_No response_
3 Comments
1|# Codex #24526: Shell Tool Appears Stuck When git push Output Not Surfaced Promptly
2|
3|## Issue Summary
4|
5|A
git pushcommand appeared to hang for several minutes even though it completed successfully. The UI showed the command as still running, but when polled later, the push output appeared immediately with successful results.6|
7|## Root Cause Analysis
8|
9|The shell tool's output streaming/polling mechanism doesn't properly surface completed command output. The process completes but the UI doesn't detect completion, leaving the command appearing "stuck" until a manual poll triggers output retrieval.
10|
11|## Proposed Fix
12|
13|### Fix 1: Improve Process Completion Detection
14|
15|``
typescript`16|class ShellTool {
17| async execute(command: string): Promise<ShellResult> {
18| const proc = spawn(command, { shell: true });
19|
20| let output = '';
21| let completed = false;
22|
23| // Stream output in real-time
24| proc.stdout.on('data', (data) => {
25| output += data.toString();
26| this.ui.updateOutput(output); // Real-time UI update
27| });
28|
29| proc.stderr.on('data', (data) => {
30| output += data.toString();
31| this.ui.updateOutput(output);
32| });
33|
34| // Detect completion immediately
35| proc.on('close', (code) => {
36| completed = true;
37| this.ui.markComplete(code);
38| });
39|
40| // Don't wait for poll — resolve when process actually completes
41| return new Promise((resolve) => {
42| const checkComplete = () => {
43| if (completed) {
44| resolve({ output, exitCode: proc.exitCode });
45| } else {
46| setTimeout(checkComplete, 100);
47| }
48| };
49| checkComplete();
50| });
51| }
52|}
53|
`54|
55|### Fix 2: Add Progress Indicators for Long-Running Commands
56|
57|
typescript``58|// For commands that take >30 seconds, show progress
59|if (estimatedDuration > 30000) {
60| this.ui.showProgress({
61| command,
62| elapsed: Date.now() - startTime,
63| status: 'running',
64| outputPreview: output.slice(-200)
65| });
66|}
67|
68|
69|## Impact
70|
71|- UX: MEDIUM — Users confused about whether command is stuck
72|- Scope: Low — Affects git push and similar network commands
73|- Fix complexity: LOW — Output streaming improvement, ~80 lines
74|
Solution: Fix Shell Tool Output Streaming
Root Cause
The shell tool's output streaming mechanism doesn't promptly deliver completion signals and output to the UI. The
git pushcommand completes successfully, but the UI shows it as "stuck" because output may be buffered instead of streamed immediately, and stream close detection may not properly wait for all output to be consumed.Fix
Three-part fix:
Why This Works
Thanks for writing this up. I checked the current implementation and I think the relevant path is a bit different:
I checked current
main(8a94430b) and this seems slightly different from a simple stdout/stderr streaming bug.The current implementation is Rust, and unified exec already starts background output streaming plus an exit watcher. What I can reproduce is the model-facing polling behavior:
sh -c 'sleep 2; printf done'withyield_time_ms=500exec_commandreturns a running session with no outputwrite_stdinSo completed output can remain pending until a later poll for the tool-response path. But the UI should still receive
ExecCommandEndfrom the background watcher. If the visibleUI remains “running”, the likely issue is event delivery/rendering or hosted wrapper state, not basic process completion detection.
Approval seems like a separate state boundary: approval happens before process spawn; a running session means approval already completed.