Diagnosing GitHub Actions Jobs Stuck in Queued State
Learn how to diagnose and fix GitHub Actions jobs that stay in the 'Queued' state, focusing on runner label mismatches, concurrency bottlenecks, and offline self-hosted agents.
09 Aug 2025, 19:38 UTC

The Problem: Jobs That Never Start
When a GitHub Actions workflow is triggered, the job should transition from Queued to In Progress almost immediately. When a job remains in the Queued state indefinitely, it usually indicates a mismatch between the job's requirements and the available compute resources, or a restriction imposed by concurrency settings.
Quick Diagnostic Matrix
| Symptom | Likely Cause | Primary Check |
|---|---|---|
| "Waiting for a runner" message | Label mismatch or offline runner | Runner Settings labels |
| Job queues only during deployments | Concurrency group bottleneck | Workflow concurrency key |
| All jobs queued across organization | GitHub Service outage | GitHub Status Page |
| Job queues then cancels automatically | Timeout or dependency failure | Workflow logs/dependency graph |
Step 1: Verify Runner Availability and Labels
The most common cause for a queued job is that the GitHub scheduler cannot find a runner that matches the labels defined in the runs-on field of your YAML file.
- Check Runner Status: Navigate to
Settings > Actions > Runnersat the repository or organization level. Ensure the runner status is listed as Idle or Active. If it is Offline, the runner service on the host machine has stopped or lost network connectivity. - Validate Label Matching: Compare the labels assigned to the runner in the settings UI with the
runs-onvalue in your workflow. Labels are case-sensitive.
Example Configuration Error:
# Incorrect: Job is looking for 'gpu-runner', but the runner is labeled 'GPU-Runner'
jobs:
train-model:
runs-on: [self-hosted, gpu-runner]
steps:
- run: echo "Training..."
Step 2: Inspect Concurrency Group Limits
GitHub allows you to limit the number of concurrent jobs using the concurrency key. This is often used to ensure only one deployment happens at a time to a specific environment. If a previous job in that group is hanging or running slowly, all subsequent jobs will stay queued.
Check your workflow for a block like this:
concurrency:
group: production-deploy
cancel-in-progress: false # If false, new jobs queue behind the current one
If cancel-in-progress is set to false, a single stuck job can block your entire pipeline. To resolve this, manually cancel the running job in the Actions tab to allow the queued job to proceed.
Step 3: Check for Resource Exhaustion
If you are using GitHub-hosted runners, you may be hitting concurrency limits based on your account plan (Free, Pro, Team, or Enterprise). While rare for small teams, large organizations can exhaust their total available concurrent jobs across all repositories.
Verification: Check the Organization Settings > Actions > Runners to see if you have reached the maximum number of concurrent jobs allowed for your plan.
Fixes Based on Findings
- If Runner is Offline: SSH into the host machine and restart the runner service. For Linux systems, this is typically done via
sudo ./svc.sh startwithin the runner directory. - If Labels Mismatch: Update the
runs-onfield in your YAML to match the exact case and spelling of the runner labels. Commit and push the change to trigger a re-evaluation. - If Concurrency Bottleneck: Set
cancel-in-progress: truein your concurrency configuration to automatically kill older, redundant jobs in favor of the latest commit.
Escalation Criteria
If the following conditions are met, the issue is likely external to your configuration and requires a GitHub support ticket or waiting for service restoration:
- The runner is confirmed Idle and labels match exactly.
- No
concurrencygroups are active. - The
GitHub Statuspage reports degradation in "Actions" or "API" services. - The job remains queued for more than 30 minutes despite no other jobs running in the organization.
Verification of Fix
To verify the resolution, trigger a manual workflow dispatch (if configured) or push a small empty commit: git commit --allow-empty -m "test runner" && git push. Observe the Actions tab; the job should move from Queued to In Progress within seconds of the trigger.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.