GitHub Actions troubleshooting
Workflow does not run
YAML syntax error prevents registration — check the Actions tab for workflow
file warnings. Branch/path filters may exclude the commit
(on.push.branches, paths). Workflow disabled in
repo settings or Actions disabled org-wide. Default branch workflows only appear
after merge unless workflow_dispatch is added. Scheduled workflows
can be delayed up to an hour on free tiers.
Permission denied / 403 on GITHUB_TOKEN
Default token is read-only for fork PRs. Add explicit
permissions: at workflow or job level for packages, deployments,
or PR comments. Org policy may restrict permissions. For cross-repo
access use a PAT or GitHub App with minimal scope stored as a secret.
Secret or variable not found
Empty env var — secret name typo or secret defined at org level but not
exposed to this repo. Environment-scoped secrets require
environment: name on the job. Variables use
vars.NAME, secrets use secrets.NAME. Re-run after
adding secrets; existing runs do not pick up new values retroactively.
Self-hosted runner offline / stuck queued
Runner service stopped on the host — restart runsvc or systemd unit.
Labels on runs-on: must match runner labels exactly. One runner
handles one job; scale out for parallelism. Offline runners show in Settings →
Actions → Runners. Check firewall to github.com and
actions.githubusercontent.com.
Action failed / version broke
Third-party action updated and changed inputs — pin to commit SHA. Node runtime
deprecation on hosted images breaks old actions; upgrade @v4 tags.
"Unable to resolve action" — typo in uses: or private action
without auth. Compare last green run’s action versions to current workflow.
Docker / registry push fails
Login step missing or wrong registry URL. Harbor and other registries need
credentials in secrets — see Harbor lab and
Docker lab. docker build on hosted
runners works out of the box; DinD or larger images may need self-hosted runners.
Required check never completes
Branch protection lists a check name that no longer matches the job name (renamed job or workflow). Skipped jobs from path filters do not satisfy required checks — use a lightweight always-run job or adjust filters. Re-run failed jobs from the Actions UI; merge queue may wait on stale pending checks.
Slow runs / minute quota
Cache misses, cold npm/pip installs, and large matrix multiply minutes. Use
actions/cache and path filters. Private repos have monthly minute
limits — monitor Usage under billing settings. macOS runners cost more minutes
than Linux.
Debugging workflow
1. Run logs
# Actions tab → failed run → job → expand failed step
# Download log archive for long outputs2. Enable debug
# Repo secret ACTIONS_STEP_DEBUG=true
# Re-run job; verbose step output in logs3. CLI from laptop
gh run list
gh run view RUN_ID --log-failed4. Run locally with act
To run and debug workflows on your machine without pushing, use
nektos/act.
It reads .github/workflows/, pulls or builds runner images via Docker,
and executes jobs with a filesystem and env similar to GitHub-hosted runners.
act -l # list workflows
act push # simulate push event
act -j test # run one job
act -s MY_SECRET=value # pass secrets locally