Codex App branch switch uses a different worktree than the task terminal
Summary
The Codex macOS app can appear to use different Git worktree contexts for the terminal and branch-switch UI within the same task.
Reproduction
- Start a Codex task from a Git repository using a managed worktree.
- In the task terminal, run:
```
pwd
git branch --show-current
/Users/<user>/.codex/worktrees/7e31/WantPic
The terminal reports a managed worktree path such as and branch ft/blog_image`.
- In the Codex app's branch switcher, select
ft/blog_image. - The app reports:
````
fatal: 'ft/blog_image' is already used by worktree at '/Users/<user>/.codex/worktrees/WantPic'
git worktree listshows the branch is already attached to the managed task worktree, while the main checkout remains on another branch.- Restarting the ChatGPT/Codex app initially restores the expected branch, but resuming/reconnecting to this task causes the wrong branch context to return.
Expected behavior
The branch controls and task terminal should resolve the same repository/worktree context. Resuming a task should preserve its associated worktree and branch.
Actual behavior
The task environment can alternate between the managed feature branch and the main branch. The branch-switch operation appears to resolve a different or stale worktree path and rejects the feature branch as already in use.
Screenshot evidence
The supplied screenshots show the following sequence:
Screenshot 2026-08-08 at 10.54.19 PM.png: the Environment panel shows1.10.0/master.Screenshot 2026-08-08 at 10.54.47 PM.png: it showsft/blog_image, the commit[1.10.0] 블로그 썸네일 추가, and one comment.Screenshot 2026-08-08 at 10.55.05 PM.png: it has returned to1.10.0/master.
This alternating state occurs while working with the same task and is consistent with the task being rebound to the wrong worktree after resume/reconnect.
Additional context (hypothesis)
The problem did not occur when the worktree and task were first created. It may correlate with reconnecting to the repository remotely and/or reopening the task from a remote session. This is a suspicion rather than a confirmed cause, but it may help narrow down when the repository/worktree context becomes stale or inconsistent.
Environment
- ChatGPT macOS app (Version 26.803.41515)
- macOS (26.5.2)
- Git worktrees enabled
<img width="314" height="498" alt="Image" src="https://github.com/user-attachments/assets/d2809e03-972c-4f08-aac6-8cc6908fdfaf" />
<img width="321" height="370" alt="Image" src="https://github.com/user-attachments/assets/9b6ffd3c-468f-4aea-a5f9-f3cc5de638a1" />
<img width="316" height="400" alt="Image" src="https://github.com/user-attachments/assets/6e38f4d2-e0c9-4b4b-a4a4-62952c78bda7" />
3 Comments
The exact same issue is happening for me. When I create a new chat with worktrees enabled, a new worktree gets created and the agent operates in it. But the UI widget shows the default worktree's state, and the "Create and checkout new branch" action in the widget operates on the default worktree instead of the actually active one. It started happening after the latest update.
Codex version 26.803.41515, macOS Tahoe 26.5
Not sure about the bug, but this is how I "fixed" the issue.
The section below was written by ChatGPT.
---
The task terminal was correctly running in a managed worktree on the feature branch, but the Environment/branch UI resolved the saved repository checkout instead. In my case, the task metadata and terminal pointed to the OCR worktree and codex/ocr/main, while the UI showed the main checkout and 1.15.2/master. The worktree itself was registered correctly in git worktree list.
What fixed it was re-binding the task through the handoff workflow:
I did not delete or recreate the worktree, and I did not manually move the branch. The handoff preserved the existing uncommitted change. This suggests a stale task-to-worktree association after resume/reconnect; I cannot confirm the underlying cause, but forcing the local-checkout -> managed-worktree rebind cleared it.
This is what Codex came up with when I sent it to investigate:
Status:
DONE_WITH_CONCERNS— the release and faulty path are high-confidence; the desktop UI’s original TypeScript source is not public, so I cannot name its internal source commit.Conclusion
The regression was introduced in Codex desktop 26.803.41515, published August 7, 2026. The immediately preceding production release, 26.730.61639, does not contain the triggering behavior.
The bug is in the desktop renderer’s new “follow an existing thread owner” resume flow—not Git and probably not the open-source Rust app-server.
| Desktop release | Embedded Codex | Relevant behavior |
|---|---:|---|
| 26.730.61639 | 0.147.0-alpha.1.2 | Normal thread hydration |
| 26.803.41515 | 0.147.0-alpha.6.5 | Adds
followExistingOwner: trueand the faulty early return || 26.803.61601 | 0.147.0-alpha.6.5 | Still contains the bug |
This matches the symptoms and version reported in [issue #37591](https://github.com/openai/codex/issues/37591).
Actual cause
The new release changed automatic task resume to send:
When another renderer already owns the task, the resume manager now:
That return happens before the normal
thread/read,thread/resume, CWD reconciliation, environment hydration, and workspace-state synchronization.Consequently:
The exact new call is visible in the extracted current bundle at [/private/tmp/use-resume.js](/private/tmp/use-resume.js). The preceding release’s equivalent bundle, [/private/tmp/use-resume-prev.js](/private/tmp/use-resume-prev.js), has no
followExistingOwnerfield or owner-discovery shortcut. The early-return implementation is readable in [/private/tmp/chatgpt-app-initial.pretty.js:160660](/private/tmp/chatgpt-app-initial.pretty.js:160660).That explains all the distinctive observations:
A second, latent CWD bug
Both releases also contain this approximate resolver:
For sibling managed worktrees such as:
the requested directory is not a descendant of the stored directory, so the stale stored directory wins. I reproduced that result directly.
This logic predates 26.803.41515, so it did not create the release boundary, but it can reinforce the same failure during a full resume.
There is also a relevant newer open-source change, [#37198](https://github.com/openai/codex/commit/547080e4d690cdeea12f427a8d9c5165928821ed), which explicitly allows
ThreadResumeResponse.thread.cwdto be a persisted CWD while the top-levelcwdis the live resumed CWD. That commit was first included in 0.148.0-alpha.1 and was not in the affected 26.803.41515 build, so it is not the original cause. It does, however, make it especially important that the desktop stops treating those two fields as interchangeable. The dual fields are visible in [thread.rs](/Users/cztamas/dev/tools/codex/codex-rs/app-server-protocol/src/protocol/v2/thread.rs:405), and the live response CWD is populated in [thread_processor.rs](/Users/cztamas/dev/tools/codex/codex-rs/app-server/src/request_processors/thread_processor.rs:3462).Suggested fix
The primary fix belongs in the desktop follower-adoption flow:
followerand return.A safe short-term hotfix is to remove
followExistingOwner: trueuntil follower workspace synchronization is implemented.The CWD resolver should also be separated by phase:
thread.cwdshould be treated as persisted metadata, not an equivalent alternative to the top-level liveresponse.cwd.The regression test should create two sibling worktrees, seed stale catalog/thread metadata, open a second renderer as a follower, and assert that terminal CWD, environment CWD, Git root, and branch-switch command CWD all remain the managed task worktree after repeated reconnects.
No repository files were changed. One limitation is structural: OpenAI documents the CLI and app-server as open-source components, but the full desktop renderer is not part of the repository, so the desktop finding came from comparing the signed application bundles rather than source history. [OpenAI’s component list](https://learn.chatgpt.com/docs/open-source) confirms the public repository boundary.