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`.