Introduction

GitHub Actions workflows are designed to trigger automatically based on events like push, pull requests, or scheduled jobs. However, workflows may occasionally get stuck in a queued or pending state due to concurrency limits, misconfigured permissions, resource availability, or incorrect workflow settings. This issue is particularly problematic for teams relying on automated deployments or frequent CI builds. This article explores the causes, debugging techniques, and solutions for resolving stuck GitHub Actions workflows.

Common Causes of Stuck or Pending Workflows

1. GitHub Actions Workflow Concurrency Limits

GitHub has a concurrency limit on workflows for free-tier accounts and self-hosted runners, which can cause jobs to remain in a queued state.

Solution: Check Current Running Jobs and Limit Concurrency

gh run list --workflow=my-workflow.yml

To limit concurrent runs, add the `concurrency` key to the workflow file:

concurrency:
  group: workflow-${{ github.ref }}
  cancel-in-progress: true

2. Lack of Available GitHub Runners

Public GitHub-hosted runners may be at capacity, causing new jobs to wait indefinitely.

Solution: Use Self-Hosted Runners or Increase Runner Pool

gh runner list

To add a self-hosted runner, register it using:

actions-runner/register.sh

3. Workflow Execution Permissions Issues

GitHub requires proper permissions for workflows triggered by `pull_request` or `workflow_dispatch` events.

Solution: Enable Workflow Permissions in Repository Settings

Navigate to **Settings → Actions** and ensure workflows are allowed.

Alternative: Manually Approve Workflow Execution

gh run review --approve

4. Stale GitHub API Tokens in Secrets

Expired or revoked GitHub API tokens (`GITHUB_TOKEN`, `PAT`) can cause workflows to remain stuck.

Solution: Regenerate and Update API Tokens

gh auth refresh

Update tokens in repository settings under **Settings → Secrets and variables → Actions**.

5. Workflow Configuration Errors

Incorrect event triggers, missing jobs, or syntax errors in `.github/workflows/*.yml` can prevent workflow execution.

Solution: Validate Workflow Syntax

gh workflow lint .github/workflows/my-workflow.yml

Debugging Stuck or Pending GitHub Actions Workflows

1. Checking Workflow Logs

gh run view --log

2. Inspecting Workflow Events

gh api repos/:owner/:repo/actions/runs

3. Monitoring Runner Availability

gh runner list

4. Manually Triggering Workflows

gh workflow run my-workflow.yml

Preventative Measures

1. Use the `concurrency` Key to Prevent Workflow Overlap

concurrency:
  group: workflow-${{ github.ref }}
  cancel-in-progress: true

2. Monitor Actions Runners and Scale Accordingly

gh runner list

3. Keep API Tokens and Secrets Updated

gh auth refresh

4. Regularly Validate Workflow Syntax

gh workflow lint .github/workflows/my-workflow.yml

5. Set Up Alerts for Stuck Workflows

gh api repos/:owner/:repo/actions/runs --jq '.workflow_runs[] | select(.status == "queued")'

Conclusion

Stuck or pending GitHub Actions workflows can delay CI/CD pipelines and deployments. By understanding common causes such as concurrency limits, unavailable runners, API token issues, and incorrect workflow configurations, developers can quickly diagnose and resolve these issues. Using tools like `gh run`, workflow permissions, and concurrency settings ensures smoother and more reliable GitHub Actions execution.

Frequently Asked Questions

1. Why is my GitHub Actions workflow stuck in queued state?

The workflow might be waiting for an available runner, blocked by concurrency limits, or missing necessary permissions.

2. How do I force-start a stuck GitHub Actions workflow?

Use `gh workflow run my-workflow.yml` to manually trigger execution.

3. Can I speed up GitHub Actions execution?

Yes, using self-hosted runners, reducing unnecessary jobs, and limiting concurrency can improve execution times.

4. How do I check why my workflow is stuck?

Run `gh run view --log` to inspect logs and find the root cause.

5. How do I prevent GitHub Actions workflows from getting stuck?

Set concurrency limits, regularly update secrets, and monitor runner availability using `gh runner list`.