A red CI check means a configured command did not complete successfully in the runner environment. It does not tell you yet whether the code is wrong, the environment differs, a dependency service is missing, or the workflow itself is misconfigured.
Use the run’s job and step logs to locate the first failing command. GitHub Actions records logs by job and step, so the visible summary is a map to the evidence, not a diagnosis on its own.
A quick decision tree
Did the workflow start?
├─ No → Check trigger conditions, workflow syntax, permissions, and skipped status.
└─ Yes → Which first command failed?
├─ Install / download → Check lockfile, registry access, cache, and runtime version.
├─ Test / type check → Compare code, fixtures, time, locale, and runtime inputs.
├─ Database / service → Confirm the service starts and is healthy before the test.
├─ Build → Inspect required variables, generated files, and exact build output.
└─ Deploy / publish → Check credentials, target environment, and provider output.
The first failure matters because later steps may fail only because earlier setup did not finish.
Read the failure in order
- Open the failed job and identify the first red step.
- Read a few lines before and after the first error. Look for the exact command, exit code, runtime version, and service names.
- Check whether the step was skipped, canceled, timed out, or actually failed. Those states have different causes.
- Compare the workflow file and lockfile with the commit being tested. A local uncommitted fix cannot explain a remote run.
- Reproduce the command locally when the environment permits it. Record what is different before changing anything.
Illustrative failure: the code is not the first problem
Run npm test
Error: connect ECONNREFUSED 127.0.0.1:5432
at openDatabase (test/helpers/database.ts:18:11)
It is tempting to blame the latest database query. First inspect whether the test job starts its database service and waits for it to become healthy. If the service is missing or not ready, the application code has not yet been meaningfully tested. Reproduce with the same database version and setup rather than changing the query.
If the database is running, check the next evidence: connection host, port, credentials, schema setup, and whether the failing test is order-dependent. Change one thing, rerun the focused test, and keep the original log for comparison.
Common cause groups
| Log points to | Compare first |
|---|---|
| Install or module resolution | Node/package-manager version, lockfile, registry, cache key |
| Test assertion | Test data, timezone, locale, randomness, concurrency, missing fixture |
| Connection refused | Service container, port mapping, startup health check |
| Missing variable | Whether the variable belongs in CI, or the test should use a safe fixture |
| Permission denied | File mode, runner user, token scope, repository permissions |
| Timeout | Actual runtime, deadlock, external dependency, insufficient readiness wait |
Do not fix a missing test variable by copying a production secret into a workflow. Determine whether it is safe and necessary for a test; prefer a non-sensitive fixture or mock when possible.
Ask AI for hypotheses, then test them
You can paste a redacted log excerpt and the relevant workflow step into an approved coding assistant. Ask for a short ranked list of hypotheses and a command or observation that would distinguish each. Do not include credentials, user records, private source code outside approved tools, or unrestricted CI logs. Treat the answer as a proposal. The log and a discriminating experiment provide evidence.
After the fix
- Re-run the narrow command and the full CI job when practical.
- Check that the fix addresses the cause and does not hide the failure with retries or skipped tests.
- If the runner environment was wrong, fix its setup and document any required service or variable.
- Share the failing step, root cause, and verification in the pull request.
Practice
Pick a CI failure from a project you can safely inspect. Write down: first failing step, exact error, local command to reproduce, one environmental difference, and the next experiment. Ask someone else whether your experiment would distinguish the top two hypotheses.