When a GitHub Actions workflow fails, the red status is only the starting point. This guide provides a repeatable way to inspect logs, isolate whether the problem is a trigger, permission, dependency, cache, secret, runner, or deployment issue, and keep a small set of workflow health signals under review.
Overview
GitHub Actions debugging is easier when you treat a workflow as a chain of separate boundaries rather than one large automated task. A run must be triggered by the expected event, obtain the required permissions, start on a compatible runner, install the right dependencies, execute each command successfully, and pass artifacts or outputs to later jobs. A failure at any boundary can make a later step appear responsible even when the underlying cause is earlier.
Start with the first failed command, not necessarily the last red step. Expand the failed job, read the log immediately before the error, and note the event that started the run, the branch or tag, the commit SHA, the runner label, and the workflow file revision. These details establish the context needed to reproduce the problem.
A useful debugging record contains four items:
- Run identity: workflow name, run number, event, ref, commit, and actor.
- Failure boundary: the first job and step that returned an error.
- Environment: runner type, operating system, runtime versions, and relevant dependency lockfile.
- Recent changes: edits to workflow YAML, source code, dependencies, secrets, permissions, or deployment configuration.
For related build failures outside the workflow itself, compare this process with the Dockerfile linter and container debugging guide. Keeping application, container, and CI failures distinct prevents a long investigation from mixing unrelated symptoms.
What to track
Track recurring variables that can explain why a workflow becomes unreliable. You do not need a complex observability system; a short record in the repository, issue tracker, or team notes is often enough.
1. Trigger and event context
Confirm that the workflow ran for the event you intended. Review branch and path filters, pull request versus push behavior, manual inputs, scheduled triggers, and conditions such as if:. A workflow that appears broken may simply be skipping a job because a condition evaluates to false.
2. Permissions and identity
Record which job needs access to repository contents, packages, pull requests, deployments, or other resources. A command that works locally can fail in CI when its token has insufficient permissions. Check workflow-level and job-level permission settings, then verify that the step is using the expected token or credential. Grant only the access required for the task.
3. Dependencies and runtime versions
Track the language version, package manager, lockfile, operating system, and major setup actions used by each job. If a dependency installation fails, inspect the exact package-manager command and its preceding setup steps. A missing lockfile, an unexpected working directory, or a runtime mismatch can look like a package problem.
When debugging structured request data or generated configuration, a JSON formatter and validator can help identify malformed output before it reaches a later step. For SQL generated during tests or migrations, the SQL formatter guide offers a practical review workflow.
4. Caches
Track the cache key, restore-key strategy, dependency-file hash, and the point at which the cache is restored. A cache hit is not proof that the contents are correct. Stale or incomplete cached data can produce failures that disappear when the cache is bypassed.
Use a controlled comparison: rerun once with the cache disabled or with a deliberately changed key, then compare the result. If the clean run succeeds, update the cache inputs or invalidate the problematic entry rather than repeatedly retrying the same run.
5. Secrets and configuration
Track the name and scope of each required secret without recording its value. Confirm that the secret exists in the repository or environment where the run executes, that the job is allowed to use it, and that the event type permits the required access. Pull request workflows from untrusted contexts may not receive the same credentials as trusted branch runs.
Never print secret values while debugging. Avoid commands that dump the complete environment, and inspect only whether a value is present or whether a dependent command accepts it. Authentication failures involving tokens can be separated from CI failures with the JWT decoder and debugging guide, but do not paste live credentials into an online tool.
6. Runners, artifacts, and deployments
Record the runner label, operating system assumptions, filesystem paths, shell, permissions, and available tools. Differences between local machines and runners commonly appear in path casing, executable permissions, installed system packages, and default shells.
For multi-job workflows, verify that artifacts and outputs are uploaded, named, downloaded, and referenced consistently. A deployment failure should be checked separately from a successful build: confirm the package exists, the target environment is selected, and the deployment credential is available at the point of use.
Cadence and checkpoints
Use a lightweight review cadence rather than waiting for a major outage.
- For every failed run: capture the first failing step, error text, event context, commit, runner, and recent changes. Record whether a rerun produced the same result.
- Monthly: review recurring failures, long-running jobs, cache behavior, flaky tests, unused secrets, and steps that depend on implicit runner state.
- Quarterly: review action versions, runner labels, runtime versions, permission boundaries, dependency installation, deployment assumptions, and documentation. Test a clean run without relying on an existing cache.
- After repository changes: revisit the workflow when changing package managers, lockfiles, default branches, monorepo paths, build outputs, environments, or deployment targets.
Keep a baseline for each important workflow: typical duration, expected jobs, required secrets, cache usage, and the artifacts it produces. The baseline does not need to be a precise service-level objective. Its purpose is to make meaningful deviations visible.
How to interpret changes
Use the failure symptom as a direction for investigation, not as a final diagnosis.
| Symptom | First checks | Likely boundary |
|---|---|---|
| The workflow does not start | Event, branch, path filters, YAML syntax, disabled workflow, and permissions | Trigger or workflow configuration |
| A job is skipped | if: expressions, outputs from earlier jobs, event type, and changed paths | Condition or dependency graph |
| Checkout or API access fails | Token permissions, repository reference, ref name, and network-dependent commands | Identity or repository access |
| Dependency installation fails | Runtime setup, lockfile, working directory, registry configuration, and cache | Environment or dependency state |
| A test fails only in CI | Environment variables, timezone, filesystem assumptions, parallelism, and runner OS | Reproducibility or test isolation |
| A rerun passes without code changes | Cache state, external service, timing, concurrency, and flaky tests | Transient dependency or nondeterminism |
| Deployment fails after build success | Artifact contents, environment selection, credentials, target URL, and permissions | Release or deployment boundary |
Compare one failed run with one successful run from the same workflow. Look for changed inputs rather than rereading every line. Differences in event type, runner, dependency cache, permissions, or secret availability often narrow the search quickly.
Add temporary diagnostics only where they answer a specific question. Useful diagnostics include the current directory, selected runtime versions, file listings for non-sensitive paths, resolved configuration names, and whether an expected variable is set. Remove noisy debugging commands after the cause is confirmed.
When to revisit
Revisit this debugging checklist whenever a workflow fails after a code or configuration change, when a dependency is upgraded, when a runner label or operating system changes, or when a repository adopts a new environment or deployment target. It is also worth reviewing after repeated reruns, because a passing rerun can hide cache corruption, timing sensitivity, or an external dependency that is not under the workflow's control.
For a practical next pass, choose one important workflow and create a small health record with these fields:
- Last known successful commit and run.
- Current average or typical duration.
- Most recent failure boundary and error message.
- Required secrets and their owning environment, without values.
- Cache key inputs and the last clean-cache test.
- Runner, runtime, package manager, and lockfile versions.
- Artifact and deployment checks that follow the build.
Then run the workflow once under normal conditions and once after changing only the suspected variable. This controlled comparison is more informative than repeated blind retries. If the issue involves API requests, authentication, or generated data, use focused tools and safe test values; the API testing workflow guide can help separate request construction from CI execution.
Finally, update the workflow's comments or runbook with the confirmed cause and the smallest reliable fix. A good GitHub Actions troubleshooting process does more than restore one failed run: it leaves the next failure with a clearer boundary, a known checkpoint, and fewer assumptions to investigate.