GitHub Actions Job-Level vs Workflow-Level concurrency Scope Mismatch
Runs serialize or cancel more (or less) than intended because concurrency was set at the wrong level. A top-level concurrency governs the entire run; a job-level concurrency governs only that job.
What this error means
A whole workflow is queued or cancelled when you only meant to serialize one deploy job, or two jobs contend unexpectedly because the group was declared at the workflow level.
concurrency: deploy # workflow level - serializes the ENTIRE run
jobs:
build: { runs-on: ubuntu-latest, steps: [{ run: make }] }
deploy: { runs-on: ubuntu-latest, steps: [{ run: ./deploy.sh }] }Common causes
Top-level group serializes everything
A workflow-level concurrency block puts the whole run in one group, so even unrelated jobs (build) contend with the one you meant to serialize (deploy).
Expecting job isolation from a workflow group
A workflow-level group cannot serialize just one job. Per-job control requires a concurrency block on that job.
How to fix it
Put concurrency on the job that needs it
Scope the group to the deploy job so build is unaffected.
jobs:
build: { runs-on: ubuntu-latest, steps: [{ run: make }] }
deploy:
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: false
runs-on: ubuntu-latest
steps: [{ run: ./deploy.sh }]Use workflow-level only for whole-run control
- Apply top-level concurrency when you want to supersede entire runs (e.g. fast CI).
- Apply job-level concurrency to serialize a single sensitive job like deploy.
- Key groups on the ref/environment so unrelated work does not contend.
How to prevent it
- Decide whether you need whole-run or single-job serialization, then set the level accordingly.
- Use job-level concurrency for deploys so CD does not block CI.
- Key concurrency groups on ref or environment to avoid cross-contention.