---
title: "GitHub Actions job stuck queued | StarSling"
description: "A job shows Queued or runs silently with no log lines. Check concurrency groups, superseded runs, job timeouts, container waits, and runner labels."
url: https://starsling.dev/github-actions/problems/github-actions-jobs-stuck
canonicalUrl: https://starsling.dev/github-actions/problems/github-actions-jobs-stuck
---

# A GitHub Actions job stays queued or hangs with no output

[GitHub Actions](https://starsling.dev/github-actions) / [Problems](https://starsling.dev/github-actions/problems) / A GitHub Actions job stays queued or hangs with no output

- [How StarSling works](https://starsling.dev/)

Diagnosis mode: runtime. Last updated: 2026-08-31

A job that never leaves Queued is waiting on something: a concurrency group, a required environment approval, or a runner label with no capacity. A job that is running but silent is usually a hung step with no timeout-minutes, or a fixed wait for a service container. The fix depends on which one it is.

## Table of contents

- [Symptoms](#symptoms)
- [How to diagnose it](#how-to-diagnose-it)
- [Likely causes](#likely-causes)
- [Hand it to an agent](#verify)
- [Related pages](#related-pages)
- [Related symptoms](#related-symptoms)
- [Sources](#sources)

<a id="symptoms"></a>

## Symptoms

GitHub only starts a job once it has both an eligible runner and a clear path through any concurrency group and environment gates you have configured. When a job sits at Queued or Waiting for a runner, the run history and the Actions UI tell you which of those it is stuck on, the workflow file alone will not, because none of these are visible as YAML defects: a concurrency group is working exactly as configured, a runner label with a small pool is a capacity fact about your account, and a required environment approval is a policy, not a bug. A job that has already started and shows no new log lines is a different problem: it has a runner, but a step is blocked on something that never returns, most often because there is no timeout-minutes to kill it, or because it is waiting a fixed amount of time for a database or cache container that was not actually ready yet. The five causes below are ordered by how each one presents, starting with the two queue-blocking causes you rule in or out from the run list, then the running-but-silent causes you rule in or out from the job log and its `runs-on` label.

- The job has said Queued or Waiting for a runner for several minutes and the progress bar has not moved.
- Other jobs for the same branch or pull request are running or queued at the same time you expected this one to start.
- The job shows as In progress, but the log panel has not printed a new line in a long time.
- The run eventually times out at exactly 6 hours with no clear failure message.
- The job only gets stuck on a specific runner label, such as macOS, Windows, arm64, or a self-hosted label, while jobs on the default Linux runner start immediately.

<a id="how-to-diagnose-it"></a>

## How to diagnose it

[ci-speedup](https://starsling.dev/ci-speedup) carries the detection logic behind the causes below and opens the fix as a reviewable pull request. Install it with `npx skills add starslingdev/skills`, then run `/ci-speedup` in your repository.

To check by hand:

1. Open the run in the Actions UI first. If it shows a `Review pending deployments` banner or a padlock icon next to an environment name, the job is waiting on a required environment approval, not a queue or capacity problem, resolve it from the environment's required reviewers, not from any cause below.
2. If there is no approval gate, pull the job list for the run: `gh api "repos/{owner}/{repo}/actions/runs/{run_id}/jobs" --jq '.jobs[] | {name, status, runner_name}'`. A job with `"status": "queued"` and a null `runner_name` has not attached to a runner yet, move to the queue-side causes. Do not discriminate on `started_at`: GitHub stamps it on queued jobs too, so it is never null and a check waiting for null will never fire.
3. For a queued job, list other runs on the same branch with `gh api "repos/{owner}/{repo}/actions/runs?branch=<branch>"` - quoted, because an unquoted `?` is a shell glob character - and check for an in-progress or queued run that overlaps it, that points to the concurrency-group or superseded-run causes.
4. Check the queued job's `runs-on` label in the same jobs response. A non-default label (Windows, macOS, arm64, or a self-hosted label) with no online runner registered explains the wait on its own, independent of concurrency.
5. If the job's `status` is `"in_progress"` instead, open its log and note the timestamp of the last printed line. A large gap with no new output means it is not queued at all, move to the timeout and container-healthcheck causes.
6. For an in-progress, silent job, check whether the current step is a service container step or a step immediately after one; a fixed `sleep` before that step is the most common source of unexplained silence.

<a id="likely-causes"></a>

## Likely causes

Ordered by how often each one turns out to be the answer. Confirm a cause with its check before you change anything.

### 1. The run is queued behind a concurrency group

A `concurrency:` block with `cancel-in-progress: false` (or no cancel setting) lets one workflow run at a time per group, so a new run for the same branch waits for the current one to finish rather than replacing it. If the group key is scoped too broadly, unrelated branches can end up sharing one slot and queuing behind each other.

Confirm it: In the Actions UI, open the run and look for a banner naming the concurrency group it is waiting on. Or list runs for the branch: `gh api repos/{owner}/{repo}/actions/runs --jq '.workflow_runs[] | select(.head_branch=="<branch>") | {id,status,run_started_at}'` and see if an earlier run is still `in_progress` or `queued`.

Fix: [Concurrency groups](https://starsling.dev/best-practices/github-actions/cut-queue-time) (`ci.trigger.concurrency-groups`, detection mode runtime)

### 2. Superseded runs from rapid pushes are backed up in the queue

Pushing several commits to the same branch in quick succession without `cancel-in-progress: true` leaves every earlier run occupying a runner slot instead of being cancelled. The newest run then queues behind runs whose results nobody needs anymore, which reads as the same job being stuck even though nothing is actually broken.

Confirm it: `gh api repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs --jq '.workflow_runs[] | select(.head_branch=="<branch>") | {id,status,created_at,run_started_at}'` and check whether multiple runs for the branch overlap in time instead of one finishing before the next starts.

Fix: [Superseded runs cancelled](https://starsling.dev/best-practices/github-actions/cancel-superseded-runs) (`ci.trigger.cancel-superseded`, detection mode hybrid)

### 3. The job is running but a step is hung with no timeout to kill it

A job without `timeout-minutes` defaults to a 6-hour ceiling, so a step that never returns (a process waiting on input, a network call with no client-side timeout, a deadlocked test) can occupy the runner for hours while producing no new log output. This looks identical to a queueing problem from the PR checks list, but the job has already started.

Confirm it: Open the job's log and compare the timestamp of the last printed line to the current time. If the job status is `in_progress` with a large gap and no new lines, pull the job definition and check for `timeout-minutes`: `gh api repos/{owner}/{repo}/actions/jobs/{job_id}` and look at `status`, `started_at`, and how long it has run relative to any timeout set in the workflow file.

Fix: [Job timeouts](https://starsling.dev/best-practices/github-actions/bound-job-timeouts) (`ci.hygiene.job-timeouts`, detection mode static)

### 4. The job is waiting on a service container that never signalled ready

A workflow that starts a Postgres, Redis, or similar service container and waits with a fixed `sleep N` instead of a healthcheck can stall silently if the container takes longer than N seconds to accept connections. The job keeps running, but produces no output while the sleep or the first dependent step retries against a port that is not open yet.

Confirm it: `grep -n 'sleep [0-9]' .github/workflows/*.yml` for the workflow in question, and check whether the `services:` block for that container defines `options: --health-cmd`. If there is a sleep instead of a healthcheck, that is the wait you are seeing in the silent log.

Fix: [Container healthchecks](https://starsling.dev/best-practices/github-actions/wait-for-container-healthchecks) (`ci.hygiene.container-healthchecks`, detection mode static)

### 5. The job is queued on a runner label with a small capacity pool

Non-default `runs-on` labels, Windows, macOS, arm64, or a specific self-hosted label, draw from a much smaller pool of available runners than the default Linux hosted runner. A job pinned to one of those labels can queue for a long time even with no concurrency group involved, and a self-hosted label with no online runner registered queues indefinitely.

Confirm it: `gh api "repos/{owner}/{repo}/actions/runs/{run_id}/jobs" --jq '.jobs[] | {name, status, labels, runner_name}'` to see the exact `runs-on` label and whether `runner_name` is null while `status` is `queued` - that pair is the unassigned-runner signal, not `started_at`, which GitHub populates on queued jobs as well. For self-hosted labels, cross-check `gh api "repos/{owner}/{repo}/actions/runners" --jq '.runners[] | {name,status,labels}'` for an online runner carrying that label.

Fix: [Right-sized runners](https://starsling.dev/best-practices/github-actions/right-size-runners) (`ci.runner.right-sizing`, detection mode runtime)

<a id="verify"></a>

## Hand it to an agent

Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to run this diagnosis against your repository and report which cause it found:

A GitHub Actions job in this repository is either stuck at Queued/Waiting for a runner, or shows In progress with no new log output. Diagnose which of the following it is, using the run history and Actions REST API, not just the workflow YAML. Report the cause before changing anything. First, check the run in the Actions UI for a required-environment-approval banner, if present, stop there, it is not one of the causes below. Otherwise call `GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs` for the stuck job. If `status` is `queued` with a null `runner_name` (not a null `started_at` - GitHub stamps that on queued jobs too): check for an overlapping in-progress or queued run on the same branch (points to a missing or overly narrow `concurrency:` block, or missing `cancel-in-progress: true`), and separately check the job's `runs-on` label against `GET /repos/{owner}/{repo}/actions/runners` for online capacity. If `status` is `in_progress`: read the job log's last timestamp, then check the workflow file for a missing `timeout-minutes` and for a `sleep` before a service-container-dependent step. Report which single cause the evidence supports.

<a id="related-pages"></a>

## Related pages

- [Cut CI queue time in GitHub Actions](https://starsling.dev/best-practices/github-actions/cut-queue-time)
- [Cancel superseded runs with concurrency and cancel-in-progress](https://starsling.dev/best-practices/github-actions/cancel-superseded-runs)
- [Bound GitHub Actions runner jobs with timeout-minutes](https://starsling.dev/best-practices/github-actions/bound-job-timeouts)
- [Wait for container healthchecks instead of sleeping](https://starsling.dev/best-practices/github-actions/wait-for-container-healthchecks)
- [Right-size GitHub Actions runners without slowing CI](https://starsling.dev/best-practices/github-actions/right-size-runners)
- [Why GitHub Actions is slow, and what actually fixes it](https://starsling.dev/github-actions-too-slow)

<a id="related-symptoms"></a>

## Related symptoms

Other symptoms on this site that share a likely cause with this one. If none of the causes above is yours, one of these is usually the page you wanted.

- [Why CI runs sit queued before any job starts](https://starsling.dev/github-actions/problems/high-queue-time)
- [A job hits a timeout, or hangs until GitHub kills it](https://starsling.dev/github-actions/problems/github-actions-timeouts)
- [CI fails, then passes on an unchanged rerun](https://starsling.dev/github-actions/problems/flaky-tests)

<a id="sources"></a>

## Sources

- [REST API: List jobs for a workflow run](https://docs.github.com/en/rest/actions/workflow-jobs)
- [Control the concurrency of workflows and jobs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/control-the-concurrency-of-workflows-and-jobs)
- [Using environments for deployment (required reviewers)](https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment)
- [Monitoring and troubleshooting self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/monitoring-and-troubleshooting-self-hosted-runners)
