## TL;DR
A job stuck at waiting for a runner to pick up this job... is queued, not broken: no runner that is online and idle carries every label the job lists in runs-on, so the scheduler never assigns it and it waits forever. Open Settings, Actions, Runners, find an idle runner, and compare its labels with the workflow. Fix the mismatch by relabeling the runner or changing runs-on to labels that exist.

```text
waiting for a runner to pick up this job...
```

## Use this when
- A GitHub Actions job sits queued showing waiting for a runner to pick up this job and never starts
- You use self-hosted runners or custom runs-on labels
- The job waits on some runs but starts on others, which points at one runner pool, not the workflow

## Not for this skill when
- The job fails immediately after starting. That is a workflow or code error, not a runner shortage
- All jobs are queued, including plain ubuntu-latest jobs. That is likely a GitHub incident; check the GitHub status page first

## Steps

1. Open the repo or org Settings, Actions, Runners page and look for an idle runner.

Expected: you see which runners are online, which are busy or offline, and the labels each one carries.

2. Compare the runner labels with the workflow runs-on. A self-hosted runner only takes jobs whose labels are a subset of its own labels.

Expected: you spot the mismatch, for example runs-on asks for self-hosted plus gpu but the only idle runner carries self-hosted alone.

3. Fix the mismatch. Relabel the runner with its config script (./config.sh with the labels option) or change runs-on to labels that exist on an online runner. On GitHub-hosted runners, check the label spelling instead (ubuntu-latest, not ubuntu_latest) and check the GitHub status page for an outage.

Expected: the job starts within a minute of the next push or re-run.

4. If the labels match and it still waits, check the pool itself. A runner stuck on a zombie job shows busy forever; restart the runner service on the host. Ephemeral runners that failed to register leave no trace in the list; check the runner logs on the host.

Expected: an idle runner with the right labels exists, and the queued job is picked up on the next run.

## Variant phrasings

### waiting for a runner to pick up this job github actions
The label-matching diagnosis above: the job is queued because no online idle runner carries its full runs-on label set.

### github actions job stuck queued self-hosted runner
Check scope as well as labels: org-level runners are only visible to jobs in that org, and repo runners only to that repo. A runner in the wrong scope never sees the job.

### runner not picking up jobs labels mismatch
Steps 2 and 3: list the labels the job asks for, list the labels the idle runners carry, and make the two sets line up on one runner.

## Why it happens
The scheduler matches jobs to runners purely by label set. One wrong label, an offline runner, or a busy runner pool means the job never gets assigned; GitHub shows the waiting message instead of failing the job, so transient capacity dips recover on their own.

## Edge cases and pitfalls
- Ephemeral runners that failed to register leave no trace; check the runner logs on the host.
- Org-level runners are only visible to jobs in that org; repo runners only to that repo.
- A runner stuck on a zombie job shows busy forever; restart the runner service on the host.
- A misspelled GitHub-hosted label queues forever the same way; there is no did-you-mean check on runs-on.

## Tool compatibility
GitHub Actions with self-hosted runners (all operating systems) and GitHub-hosted runners. Runner application 2.2xx and later, per the original source.

## Related skills
- gitlab ci runner debugging tips: https://vectle.com/skills/skl_RFpi9p6tOdxxHg5DR0M15g
- agent timed out waiting for dbt cloud job run, job run stuck in queued: https://vectle.com/skills/skl_EeLVrc9mZiUz57TQjghXVg

## Provenance
Resolved from the Stack Overflow question below: a GitHub Actions job waits indefinitely showing this message, and the verified fix is matching runs-on labels to an online, idle runner.
