Skip to content

Fix: Cancelled duplicate runs block ruleset-required workflows #171

Description

Summary

Ruleset-required workflows in this repository use
concurrency.cancel-in-progress: true. When two pull_request events arrive
within about a second, two workflow runs are created for the same head SHA
and one is cancelled. Usually the cancelled run is the older one and everything
is fine. Occasionally the ordering inverts: the most recently created run is
the cancelled one, while an older run succeeded.

When that happens the required-workflows ruleset reports the workflow as
failed and the PR cannot merge — even though the check itself passed. The PR
stays blocked until something forces a fresh run.

Confirmed occurrence

lfreleng-actions/sigul-sign-docker#175, head SHA
bb87d81b6c7a8987671497c73dc219eaaef5fda2.

GraphQL state (still true at time of writing):

mergeable         : MERGEABLE
mergeStateStatus  : BLOCKED
rollup state      : FAILURE
  Semantic Pull Request / Semantic Pull Request  CANCELLED  2026-08-13T14:04:35Z
  Semantic Pull Request / Semantic Pull Request  SUCCESS    2026-08-13T14:04:58Z

Merge attempt response:

Repository rule violations found / Required workflows failed
  • Semantic Pull Request 🛠️

Workflow runs for that SHA:

Workflow Created Conclusion
Semantic Pull Request 🛠️ 14:04:32 success
Semantic Pull Request 🛠️ 14:04:33 cancelled

Note the inversion: the run that completed last is the successful one
(check run completed 14:04:58), but the run created last is the cancelled
one (created 14:04:33, cancelled 14:04:35 before the other had even started its
job at 14:04:39).

Every other workflow on the same SHA shows the same duplicate-run pattern with
the expected ordering, and none of them block:

Workflow Older run Newer run
AI Slop Scan 🧹 cancelled 14:04:31 success 14:04:33
Zizmor Scan 🌈 cancelled 14:04:31 success 14:04:32
Sigul Build/Test 🐳 cancelled 14:04:31 success 14:04:33
SHA Pinned Actions 📌 cancelled 14:04:32 success 14:04:33
Package Hardening Audit cancelled 14:04:32 success 14:04:33
Semantic Pull Request 🛠️ success 14:04:32 cancelled 14:04:33

Probable mechanism

Stated as inference — I have not found GitHub documentation that pins this down
precisely, so the maintainers may want to verify:

The required-workflows ruleset appears to evaluate the workflow run, and to
select the most recently created run for a given workflow, rather than the
most recently completed check run. In the case above that selects the
cancelled run, so the requirement is reported as failed despite a later
successful check run existing for the same SHA.

What is not in doubt: the check passed, a cancelled duplicate exists, and the
PR is blocked.

How often

Surveyed 160 recent PR head SHAs across eight repositories
(sigul-sign-docker, tag-validate-action, python-workflows,
docker-workflows, workflows-template, markdown-table-fixer, lftools-uv,
http-api-tool-docker):

  • 164 (workflow, SHA) pairs had more than one run — duplicate runs are
    routine, roughly one per SHA
  • 1 had the newest run cancelled with a success present (~0.6%)

So it is rare, but it fails closed and blocks the PR indefinitely, and with
bulk automation merges running hundreds of PRs it will recur.

Exposure

Five ruleset-required workflows in this repository currently use
cancel-in-progress: true:

File Group cancel-in-progress
aislop.yaml ${{ github.workflow }}-${{ github.ref }} true
package-hardening-audit.yaml ${{ github.workflow }}-${{ github.ref }} true
semantic-pull-request.yaml ${{ github.workflow }}-${{ github.ref }} true
sha-pinned-actions.yaml ${{ github.workflow }}-${{ github.ref }} true
zizmor.yaml ${{ github.workflow }}-${{ github.ref }} true

All five are exposed to the same race. Only semantic-pull-request.yaml has
been observed hitting it so far.

By contrast repo-audit.yaml, release.yaml, aislop-sarif-publish.yaml and
zizmor-sarif-publish.yaml already set cancel-in-progress: false.

Suggested fix

Set cancel-in-progress: false on workflows that a ruleset marks as required:

concurrency:
  group: "${{ github.workflow }}-${{ github.ref }}"
  cancel-in-progress: false

Rationale: a cancelled run of a required workflow can block a merge, so
cancellation is not a safe optimisation for this class of workflow. The cost of
letting both runs finish is small — the Semantic Pull Request job ran in
19 seconds (14:04:39 → 14:04:58). Both duplicate runs evaluate identical
inputs, so both would report the same result and no cancelled run would remain.

What will not work

Narrowing the concurrency group to include the head SHA, e.g.
${{ github.workflow }}-${{ github.event.pull_request.head.sha }}, does not
fix this. The two colliding events share a head SHA, so they would still land
in the same group and still cancel each other. The problem is the cancellation
itself, not the group key.

Possible refinement

If cancellation is worth keeping for the more expensive scans, it could be made
conditional on the event type — cancelling only on synchronize (a genuinely
superseded commit) and never on edited / reopened. That is more moving
parts than simply disabling it, so probably only worth it if the run cost
matters.

Recovering currently-stuck PRs

Re-running the cancelled workflow run, or otherwise generating a fresh run
(pushing a commit, editing the title), clears the block.
lfreleng-actions/sigul-sign-docker#175 is stuck on this right now and can
serve as a test case.

Context

Found while auditing a bulk automation-merge run over lfreleng-actions
(503 PRs). Related, but distinct:

  • lfit/releng-reusable-workflows#854 — the semantic check's Dependabot
    title/subject relaxation misses mid-string version elision. Different root
    cause; both surfaced as Semantic Pull Request 🛠️ failures in the same run.
  • lfreleng-actions/dependamerge#405 — tooling-side handling for the above.

Activity

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

    CICI and tests updatesbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions