GitHub Actions failed? Find the first real error
The red summary is rarely the diagnosis. Start with the first failing job and step, preserve the earliest concrete error, compare it with the workflow and repository state, and make the smallest change that explains the evidence.
Record the run URL, commit, event, runner, matrix values, first failing job, and first failing step. Read that step's log from the earliest concrete error forward. Classify it as a dependency, test, syntax, timeout, permission, secret, or runner problem. Check the matching workflow, manifest, lockfile, and recent diff before editing. Apply one evidence-backed change, reproduce locally when possible, then rerun only the affected job or failed jobs. Do not treat an empty secret, a retry, or one green run as broader proof than it is.
Need this diagnosed from a real run? See CI Doctor's evidence-first workflow →
Diagnosis path
1. Record the failing run before changing anything
GitHub exposes a run as jobs, and jobs as steps. Preserve that hierarchy. A failed matrix leg on a fork pull request can have a different cause from the same workflow on a push to the main repository.
Run record
- Workflow name, run URL, run ID, and attempt.
- Commit SHA, branch, and event such as
push,pull_request, orworkflow_dispatch. - First job that failed and any upstream job it depended on.
- Runner image, operating system, language version, and matrix values.
- First failing step and a short permalinked log excerpt.
- Last known passing run for the same workflow and comparable event.
GitHub's run history and visualization graph expose job and step status. Repository read access is required to inspect the logs.
2. Read the first concrete error, not the final summary
A later step can fail because checkout, dependency installation, compilation, or a service setup step already failed. Expand the earliest red step and read from its first concrete tool error forward. Ignore generic wrappers such as Process completed with exit code 1 until the underlying command explains why.
gh run list --limit 10
gh run view RUN_ID --verbose
gh run view --job JOB_ID --log-failed
GitHub documents --log-failed for failed-step output and permits a permalink to a specific log line. Save only the small excerpt that establishes the failure; do not paste an entire log containing tokens, signed URLs, customer data, or unrelated output into a ticket or model.
Job failed before any step ran, or the steps array is empty
If the run concludes with failure but the job shows no executed steps, an empty steps array is an observation, not a root cause. Do not hunt through test output that never ran. Start with the run and job annotations, then inspect the workflow at the failing commit and download the job log or runner diagnostic log when GitHub exposes one.
Pre-step checklist
- Workflow validation: invalid YAML, unsupported keys, invalid expressions, or a broken reusable-workflow reference.
- Job scheduling and initialization: an unavailable self-hosted runner label, container startup, repository or token permissions, or an Actions billing or usage block.
- Control flow: a job-level
ifcondition or failedneedsdependency normally explains a skipped job, not a failed test step; read the displayed conclusion and annotation precisely. - Evidence boundary: if no command step started, changing application tests cannot explain the current failure without additional evidence.
3. Classify one primary failure
| Class | Evidence to look for | Common false conclusion |
|---|---|---|
| Dependency | Missing module, incompatible runtime, out-of-sync lockfile, unavailable package, or registry error. | Deleting the cache will fix every install problem. |
| Test | Named assertion, snapshot, lint, type, coverage, or integration failure after setup succeeds. | The workflow YAML is broken because the job is red. |
| Syntax | Workflow parsing error with a file, line, column, or unexpected key. | The shell command inside the invalid workflow ran. |
| Timeout | Job or command reaches a configured limit, receives cancellation, or hangs at a repeatable step. | Increasing the timeout removes the underlying hang. |
| Permission | Resource not accessible, read-only token behavior, denied package or deployment operation. | Every 403 means a personal access token is required. |
| Secret | Workflow references a secret and the consuming tool reports empty or failed authentication. | The log proves whether the secret exists or reveals its value. |
| Runner | Hosted-image change, shutdown, disk pressure, missing system tool, or documented service incident. | Application code must change after one transient runner failure. |
Keep secondary symptoms in the evidence, but force the diagnosis to explain the earliest failure. If the evidence does not distinguish two causes, say what observation would.
4. Check the repository state that matches the error
Match the package manager and runtime
Read the manifest, committed lockfile, version files, and setup action. Do not recommend npm install in a pnpm repository or upgrade a runtime merely because the latest version exists.
Read the workflow at the failing commit
Review working-directory, shell, environment, cache key, permissions, service containers, matrix values, and action inputs as they existed for that SHA.
Compare the triggering diff
Ask what changed between the last comparable pass and the failure: dependency declaration, lockfile, test expectation, path, secret reference, token permission, action version, or runner image.
A fix is credible when it connects one observed error to one relevant repository fact. A list of generic CI tips is not a diagnosis.
5. Treat secrets, forks, and token permissions as separate questions
A workflow can reference ${{ secrets.NPM_TOKEN }}, but logs generally cannot prove that the secret exists in repository settings. Authentication can fail because the name is misspelled, an environment approval is pending, the credential is expired or under-scoped, or the triggering event cannot receive it.
GitHub documents that fork pull-request workflows normally receive a read-only GITHUB_TOKEN and no secrets. Workflow- and job-level permissions can further narrow the token; specifying any permissions sets unspecified permissions to none. Prefer the minimum access the job actually needs, and do not solve a fork boundary by broadly sending secrets to untrusted pull-request code.
Safe report language
The workflow references secrets.NPM_TOKEN, and the publish step returns an authentication error. The log does not prove whether the secret exists. Check the exact secret name, environment approval, event origin, and token scope before changing workflow permissions.
6. Verify one change against the original failure
- State the root cause in one sentence.
- Make one minimal workflow, dependency, test, permission, or runner change that follows from the evidence.
- Run the matching local parser, install, build, test, or lint command when the repository supports it.
- Review the diff for unrelated changes and exposed credentials.
- After approval, rerun the specific job or failed jobs with the same event conditions where possible.
- Record the passing run and confirm the original error is gone rather than merely hidden by
continue-on-error,|| true, or a disabled step.
GitHub supports rerunning a specific job or only failed jobs, with optional debug logging. A green rerun is evidence for that commit, event, matrix, runner, and dependency state. It is not proof that every branch or future run is fixed.
Do not confuse fail-fast with continue-on-error
strategy.fail-fast applies to a matrix and defaults to true: when a matrix job that is not allowed to fail fails, GitHub cancels queued and in-progress matrix jobs. Job-level continue-on-error allows that one job to fail without failing the workflow, while step-level continue-on-error applies only to that step. A canceled matrix leg is therefore not evidence that its own command failed.
Turn the failed run into an evidence-backed fix
CI DoctorA focused SKILL.md workflow that reads the first failing step, classifies one primary cause, checks the matching repository context, and produces a root-cause statement, short evidence, and a copy-paste fix. It asks before editing files, rerunning jobs, committing, pushing, or opening a pull request.See the CI diagnosis skill →Common questions
Where should I start when a GitHub Actions workflow fails?
Open the failed run, identify the first job and step that failed, and preserve the earliest concrete error plus a few surrounding lines. Later failures are often consequences of that first error.
How can I view failed GitHub Actions logs from the command line?
Use gh run list to identify the run, gh run view RUN_ID --verbose to see jobs and steps, and gh run view --job JOB_ID --log-failed for the failed-step log. Repository read access is required.
Does an authentication error prove a GitHub secret is missing?
No. It can also mean the secret name is wrong, an environment approval is pending, the token lacks scope, or the run came from a fork where secrets and write permissions are restricted. State only what the workflow and log evidence support.
Should I rerun the workflow before changing code?
Rerun without a code change only when the evidence suggests transient runner, network, or service behavior. A deterministic syntax, dependency, test, or permission failure needs a targeted fix first.
Does one green rerun prove the CI problem is fixed?
It confirms that the changed run passed under that commit, event, matrix, runner, and dependency state. Compare the failing and passing evidence and keep the scope of the claim that narrow.
Why does a GitHub Actions job show failure with no steps or an empty steps array?
It means the normal step log is not the place to start. Check the run and job annotations, workflow syntax at the failing commit, job-level conditions and dependencies, runner labels, reusable-workflow references, permissions, billing, and the downloadable job log before changing test code.
What is the difference between fail-fast and continue-on-error?
Matrix fail-fast cancels queued and in-progress matrix jobs after a non-allowed matrix job fails, and defaults to true. Job-level continue-on-error allows one job to fail without failing the workflow; step-level continue-on-error applies only to that step.
Primary references
- GitHub: use workflow run logs and failed-step output
- GitHub: view workflow run history with the CLI
- GitHub: troubleshoot workflows and enable debug logging
- GitHub: workflow syntax and token permissions
- GitHub: fork pull-request tokens, secrets, and approvals
- GitHub: rerun failed jobs or a specific job