Skip to content
Latchkey

GitHub Actions Concurrency Deadlock - Job Waits on Its Own Group

A job sits in "pending" and never starts because the concurrency group it belongs to is already occupied by a job it depends on - the dependency cannot finish until the waiting job releases the group, which it never does.

What this error means

A job shows as pending indefinitely (not queued for a runner, but blocked by concurrency), while a job it needs is also in the same concurrency group with cancel-in-progress disabled - neither can proceed.

.github/workflows/ci.yml
concurrency:
  group: deploy            # both build and deploy share this group
  cancel-in-progress: false
jobs:
  build:    { ... }
  deploy:   { needs: build, ... }   # deploy waits on build, but the group is held

Common causes

Dependent jobs share one concurrency group

When a job and a job it needs are in the same group with cancel-in-progress: false, the second waits for the group while the first holds it, producing a deadlock.

Group key too coarse across the graph

A constant group name applied job-wide (or workflow-wide) makes unrelated jobs in the dependency chain contend for the same lock.

How to fix it

Scope concurrency so dependent jobs do not collide

Put the concurrency lock only on the job that needs serialization (e.g. deploy), not on its dependencies, and key it per ref.

.github/workflows/ci.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps: [{ run: make }]
  deploy:
    needs: build
    concurrency:
      group: deploy-${{ github.ref }}   # only deploy is serialized
      cancel-in-progress: false
    runs-on: ubuntu-latest
    steps: [{ run: ./deploy.sh }]

Break the lock cycle

  1. Move the concurrency block to the single job that must not run in parallel, not the whole workflow.
  2. Use distinct group keys for build vs deploy so a needed job never shares a lock with its dependent.
  3. Include github.ref in the key so different branches do not block each other.

How to prevent it

  • Apply concurrency to the specific job that needs serialization, not its dependencies.
  • Never let a job and a job it needs share a cancel-in-progress: false group.
  • Key concurrency groups per ref/environment.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →