Codex app shows stale merged/closed PR for main branch

Open 💬 5 comments Opened May 31, 2026 by nkeat12

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

26.527.31326

What subscription do you have?

Not sure from the local app UI; paid Codex app access.

What platform is your computer?

Darwin 25.5.0 arm64 arm

What issue are you seeing?

The Codex Mac app can show an old merged/closed pull request in the Environment panel when starting a new chat on the default branch.

In my case, opening a new chat in a local repo on main briefly showed "Checking PR..." and then displayed a historical PR row in the Environment panel. The PR was not open and was not associated with current local work. Renaming the historical PR on GitHub changed the label shown in Codex after restart, confirming the app was loading live GitHub PR metadata rather than stale local state.

After intercepting the app's GitHub traffic, I found that Codex/gh was searching for PRs with this query:

{
  "limit": 30,
  "q": "author:@me head:main repo:OWNER/REPO type:pr",
  "type": "ISSUE_ADVANCED"
}

The response contained only historical PRs:

{
  "data": {
    "search": {
      "issueCount": 4,
      "nodes": [
        { "number": 310, "state": "MERGED", "url": "https://github.com/OWNER/REPO/pull/310" },
        { "number": 158, "state": "CLOSED", "url": "https://github.com/OWNER/REPO/pull/158" },
        { "number": 155, "state": "CLOSED", "url": "https://github.com/OWNER/REPO/pull/155" }
      ]
    }
  }
}

Codex then fetched PR details for the first returned result and displayed it as if it were relevant to the current chat.

This appears equivalent to running:

gh pr list \
  --repo OWNER/REPO \
  --author @me \
  --head main \
  --state all \
  --json number,url,state \
  --limit 30

Because --state all omits state:open from the GitHub search query, merged and closed PRs are returned for head:main. A stale reverse/sync PR whose head branch was main can therefore be selected and shown forever on new chats.

What steps can reproduce the bug?

  1. In a GitHub repo, have no open PR for the local default branch, for example main.
  2. Have at least one historical merged or closed PR authored by the current GitHub user where the PR head branch was main.
  3. Open the Codex Mac app in that repo and start a new chat.
  4. Watch the Environment panel.

Observed behavior:

  • The app briefly shows "Checking PR..."
  • It displays a merged/closed historical PR in the Environment panel.
  • Clicking the PR opens the historical PR on GitHub.

I verified the root cause by putting a narrow wrapper earlier in PATH for gh that rewrites only this specific lookup from --state all to --state open. After relaunching Codex, the stale PR row disappeared. The same command with the wrapper disabled reproduced the bad result.

What is the expected behavior?

Codex should only show an associated PR when the PR is currently open and relevant to the active branch/worktree.

For the PR lookup, Codex should either:

  • query only open PRs, for example gh pr list --state open ..., or
  • filter the returned PRs and ignore MERGED / CLOSED results before showing the Environment PR row.

If no open PR exists for the current branch, the Environment panel should not show a historical PR.

Additional information

I searched existing issues before drafting this and did not find an obvious duplicate for "stale pull request", "Environment PR card", "gh pr list" "--state all", or "Checking PR".

The open-source TUI path appears to do the safer thing already: codex-rs/tui/src/branch_summary.rs filters GitHub PR search results to state == open. This issue seems specific to the Codex Mac app PR-discovery path.

Temporary local workaround that confirmed the diagnosis:

# Put a wrapper before /opt/homebrew/bin/gh in PATH.
# For only this shape:
#   gh pr list --state all --head main --json number,url,state ...
# rewrite --state all to --state open.

View original on GitHub ↗

5 Comments

bmccarthyco · 2 months ago

I’m seeing the same issue on the current Codex Desktop version available to me:

  • Codex Desktop: 26.623.30605 (CFBundleVersion: 4415)
  • Platform: Darwin 25.5.0 arm64 arm

In my case this now appears on every thread in the repo UI, not just when starting a new chat. The UI shows the old merged PR under the branch/environment popover for main, as in the attached screenshot.

<img width="231" height="295" alt="Image" src="https://github.com/user-attachments/assets/4a9707f1-a739-41e3-97ed-6fd3b772dc9b" />

The PR Codex is surfacing is:

  • PR: demo/demo#2
  • State: MERGED
  • Head branch: main
  • Base branch: demo
  • Current title: demo merge

I verified local Git config is not the source at this point:

git config --get-all branch.main.github-pr-owner-number | wc -l
# 0
AndrewTriesToCode · 1 month ago

I'm new to the Codex app and am also experiencing this.. Spent way too much time trying to figure it out. Hope it gets fixed.

nikhilshinday · 1 month ago

Confirmed this is still reproducible in the current desktop build available to me:

  • Codex Desktop: 26.707.30751
  • Platform: macOS 26.1, arm64

A single historical reverse/sync PR causes every normal sidebar thread whose stored branch is main to display the Merged PR badge. The concrete PR is buildbetter-app/buildbetter#4519:

  • State: MERGED
  • Head: main
  • Base: staging
  • Merged: 2026-06-30

The repository and local main checkout are clean and synchronized. GitHub search returns exactly that historical PR for head:main.

I also inspected the installed desktop bundle. The sidebar PR chip invokes the gh-pr-status query using the thread's stored cwd and headBranch; for ordinary local threads, headBranch is main. Consequently all such threads share the same lookup result and inherit the historical merged PR badge. Worktree threads on other branches are not affected in the same way.

This makes the issue broader than an incorrect Environment-panel card: it changes the status icon for every main-branch thread in the project sidebar.

Suggested safeguards:

  1. Do not associate MERGED or CLOSED PRs with active sidebar threads during branch-based discovery.
  2. In particular, skip historical PR discovery when the head branch is the repository's default branch, unless the user explicitly opened that PR.
  3. Ideally key PR association to an open PR and/or a matching head commit rather than branch name alone.

There is no practical GitHub-side cleanup for a merged PR whose head was the long-lived default branch, so affected repositories otherwise retain the false association indefinitely.

HCD2016-hash · 1 month ago

Still reproducible on Windows in the newest Microsoft Store build available today (2026-07-16):

  • Platform: $(Win32_OperatingSystem: Microsoft Windows 11 Pro.Caption), $(Win32_OperatingSystem: Microsoft Windows 11 Pro.Version), $(Win32_OperatingSystem: Microsoft Windows 11 Pro.OSArchitecture)
  • Store package: OpenAI.Codex 26.707.9981.0 (Store ID 9PLM9XGG6VKS)
  • Bundled app version/build: 26.707.72221 / 5307
  • Bundled app server:
Colin-Bao · 26 days ago

I ran into the same issue with Codex Desktop showing a historical merged PR as associated with new sessions on the current branch.

I traced this to the PR lookup using gh pr list with --state all. This allows merged and closed PRs to be returned, after which Codex may select one of them as the current PR.

As a temporary workaround, I placed the following narrow wrapper earlier in PATH. The repository and branch values below are placeholders:

#!/usr/bin/env bash
set -euo pipefail

REAL_GH=/usr/bin/gh
TARGET_REPO="OWNER/REPO"
TARGET_HEAD="BRANCH"

args=("$@")
is_pr_list=false
matches_repo=false
matches_author=false
matches_head=false
state_index=-1

if [[ ${args[0]-} == pr && ${args[1]-} == list ]]; then
  is_pr_list=true
fi

for ((i = 0; i < ${#args[@]}; i++)); do
  case ${args[i]} in
    --repo)
      [[ ${args[i + 1]-} == "$TARGET_REPO" ]] && matches_repo=true
      ;;
    --repo=*)
      [[ ${args[i]#--repo=} == "$TARGET_REPO" ]] && matches_repo=true
      ;;
    --author)
      [[ ${args[i + 1]-} == @me ]] && matches_author=true
      ;;
    --author=@me)
      matches_author=true
      ;;
    --head)
      [[ ${args[i + 1]-} == "$TARGET_HEAD" ]] && matches_head=true
      ;;
    --head=*)
      [[ ${args[i]#--head=} == "$TARGET_HEAD" ]] && matches_head=true
      ;;
    --state)
      [[ ${args[i + 1]-} == all ]] && state_index=$((i + 1))
      ;;
    --state=all)
      state_index=$i
      ;;
  esac
done

if [[ $is_pr_list == true &&
      $matches_repo == true &&
      $matches_author == true &&
      $matches_head == true &&
      $state_index -ge 0 ]]; then
  if [[ ${args[state_index]} == --state=all ]]; then
    args[state_index]=--state=open
  else
    args[state_index]=open
  fi
fi

exec "$REAL_GH" "${args[@]}"

This changes --state all to --state open only for the specific PR lookup used by Codex. All other gh invocations are passed through unchanged.

After restarting Codex, the stale PR association disappeared. This seems to confirm that the lookup should either query only open PRs or filter out merged/closed PRs before selecting the associated PR.

I’ll remove this local workaround once the upstream fix is available.