Visitar URL original
pr status counts cancelled runs as failures despite newer successful run · Issue #12895 · cli/cli · GitHub
Skip to content

pr status counts cancelled runs as failures despite newer successful run #12895

Open

Description

@npitre

Describe the bug

When a GitHub Actions workflow uses concurrency with cancel-in-progress: true, cancelled runs are counted as failures in gh pr status and gh pr view --json statusCheckRollup, even when a newer run for the same check name has succeeded. The GitHub web UI handles this correctly and shows "All checks passed". Also gh pr checks correctly shows the check as passing.

Affected version

gh version 2.87.3 (2026-02-23)

Steps to reproduce the behavior

  1. Have a workflow with concurrency: { cancel-in-progress: true } triggered by labeled/unlabeled events
  2. Labels are changed in quick succession, causing some runs to be cancelled
  3. Run gh pr status. Expected output:
  #105183  drivers: stepper: fix doxygen grouping [user:branch]
  ✓ Checks passing
  1. Actual output:
  #105183  drivers: stepper: fix doxygen grouping [user:branch]
  × 1/47 checks failing - Review required
  1. Yet gh pr checks correctly shows the check as passing:
$ gh pr checks 105183
Prevent Merging	pass	40s	https://github.com/zephyrproject-rtos/zephyr/actions/runs/...
  1. The check rollup includes both the cancelled and successful runs:
$ gh pr view 105183 --json statusCheckRollup | jq '.statusCheckRollup[] | select(.name == "Prevent Merging") | {name, conclusion}'
{ "name": "Prevent Merging", "conclusion": "CANCELLED" }
{ "name": "Prevent Merging", "conclusion": "SUCCESS" }

Expected vs actual behavior

Expected: gh pr status should show checks as passing when the latest run for each check name has succeeded, consistent with the web UI ("All checks passed, 2 skipped 32 successful") and gh pr checks.

Actual: gh pr status counts the stale cancelled run as a failure, showing × 1/47 checks failing.

Logs

See zephyrproject-rtos/zephyr#105175 for discussion and additional affected PRs (#105182, #105183).

Activity

  1. added a commit that references this issue on Mar 11, 2026
    1c274a8
  2. BagToad commented on Mar 13, 2026

    @BagToad
    Member

    I think this misalignment in behavior between the two commands makes sense to fix. Thanks for the report @npitre

  3. added
    bugSomething isn't working
    priority-2Affects more than a few users but doesn't prevent core functions
    gh-prrelating to the gh pr command
    and removed on Mar 13, 2026
  4. blackwell-systems commented on Apr 27, 2026

    @blackwell-systems

    I'd like to work on this. The root cause is that ChecksStatus() in api/queries_pr.go iterates contexts.Nodes without deduplication, so stale cancelled runs are counted alongside the newer successful run for the same check. The fix would apply the same deduplication pattern already used in pkg/cmd/pr/checks/aggregate.go (sort by StartedAt descending, keep only the most recent entry per check name).

    Could this be labelled help wanted so I can submit a PR?

  5. npitre commented on Aug 3, 2026

    @npitre
    Author

    Still reproduces on current main. Concrete case today, zephyrproject-rtos/zephyr#115239:

    $ gh pr status
      #115239  tests: kernel: sched: keep the slice-reset thre...
      × 3/30 checks failing - Review required
    
    $ gh pr checks 115239
    20 passing, 3 skipping    (23 rows)
    

    Nothing is failing. "Prevent Merging" ran five times on the same head as different events fired, concurrency cancelled three older runs, and the newest succeeded. pr checks collapses those five to one row, pr status counts all five, hence 30 versus 23.

    Five months on, four attempts at this have come and gone:

    Could a maintainer say what would be acceptable here? Either the approach is wrong, in which case saying so once saves the next four attempts, or it is fine and the blocker is procedural, in which case adding help wanted to this issue would let a community fix through instead of the bot closing it in four days.

    One technical note that may be the reason #12906 was withdrawn, and which invalidates the two community attempts regardless of the label. For github.com, pr status never runs the code they modified: pkg/cmd/pr/status/status.go feeds CheckRunAndStatusContextCounts into pkg/cmd/pr/status/http.go, which selects the statusCheckRollupWithCountByState fragment, so ChecksStatus() takes the fast path in api/queries_pr.go where Total = CheckRunCount + StatusContextCount and the buckets come from pre-aggregated checkRunCountsByState. #13563 and #13658 added dedup to the node path, which pr view uses and pr status does not, so neither would have changed the 3/30 above.

    That fast path carries no per-check identity, so dedup is impossible there. The real choice looks like dropping the countsByState fast path for pr status and paying for the paginated node query, or approximating as #12906 did by subtracting cancelled from the total, which also hides a genuinely cancelled required check, and such a check does block merging in the web UI.

    My own view is that dedup is the right target rather than reclassifying CANCELLED: once the newest run per check wins, a stale concurrency victim disappears on its own while a real cancelled check still reads as not passing, matching the web UI. The two commands disagreeing about how many checks a PR has is the defect. Whether pr status folds skipping into passing is presentation and fine either way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggh-prrelating to the gh pr commandpriority-2Affects more than a few users but doesn't prevent core functionsstale

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions