CI Status Checker
Diagnoses CI/CD pipeline failures by reading GitHub Actions logs, identifying failure patterns, and producing actionable fix recommendations. This skill does not write workflows from scratch -- it triages what went wrong with existing ones.
Activation Triggers
Activate on: "CI failing", "build broken", "workflow error", "GitHub Actions debug", "flaky CI", "pipeline timeout", "CI red", "actions run failed", "check failed on PR", "why is CI broken"
NOT for: Writing new workflows --> github-actions-pipeline-builder | Caching strategy --> ci-cache-optimizer | Deployment pipelines --> devops-automator | Performance testing --> performance-profiler
Core Capabilities
- Read GitHub Actions run logs and extract the failing step with surrounding context
- Classify failures into known categories (dependency, build, test, timeout, OOM, permissions, config)
- Suggest targeted fixes based on error pattern matching against known CI failure modes
- Detect workflow YAML syntax and configuration issues before they cause runtime failures
- Identify flaky tests by analyzing pass/fail patterns across recent runs
- Check for common misconfigurations: wrong Node version, missing secrets, incorrect paths
- Diagnose matrix build failures where only specific combinations fail
- Trace failure cascades (step A fails silently, step B fails loudly because of A)
Diagnostic Process
Step 1: Get Run Status
# Check most recent run status
gh run list --limit 5
# Get details for a specific failed run
gh run view <run-id>
# Get the full log for a failed run
gh run view <run-id> --log-failed
Always start with --log-failed rather than --log to avoid pulling megabytes of passing step output. Only escalate to full logs if the failed step output is insufficient.
Step 2: Identify the Failing Step
Parse the log output to find the first step that failed. CI logs contain noise -- focus on:
- The step name that shows a red X
- The last 50 lines of that step's output
- The exit code (non-zero)
- Any
##[error]annotations
Step 3: Classify the Failure
Failure Classification Guide
Category 1: Dependency Installation Failures
Signals:
npm ERR!oryarn errorin install stepERESOLVE unable to resolve dependency tree404 Not Foundfor a packageENOENTfor lockfilepip installfailures with version conflictsCould not find a version that satisfies the requirement
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
ERESOLVE peer dependency conflict |
Incompatible package versions | npm install --legacy-peer-deps or fix version constraints |
404 Not Found for package |
Package unpublished or registry down | Check npm status, pin working version |
ENOENT package-lock.json |
Lockfile not committed | Commit lockfile, or use npm install instead of npm ci |
engine "node" incompatible |
Wrong Node version in CI | Match node-version in workflow to engines in package.json |
ENOMEM during install |
Runner out of memory | Use larger runner or reduce parallelism |
ETARGET no matching version |
Version range resolves to nothing | Pin specific version, check if package was yanked |
Category 2: Build Failures
Signals:
- TypeScript errors:
TS2304,TS2339,TS2345, etc. Module not foundorCannot find moduleSyntaxError: Unexpected token- Build step exits non-zero
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
TS2307: Cannot find module |
Missing type declarations | Install @types/* package or add to tsconfig.json paths |
TS2345: Argument of type |
Type mismatch introduced in PR | Fix the type error in source code |
Module not found: 'X' |
Import path wrong or package missing | Check import paths, verify package in dependencies |
JavaScript heap out of memory |
Build exceeds Node memory limit | Add NODE_OPTIONS=--max-old-space-size=4096 to env |
SIGKILL during build |
OOM killer terminated process | Use larger runner (ubuntu-latest-4-cores) or optimize build |
Category 3: Test Failures
Signals:
- Test runner reports failures with assertion messages
FAILmarkers in Jest/Vitest outputExpected X but received Y- Test timeout exceeded
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
| Assertion failure with clear diff | Real bug introduced in PR | Fix the code (not the test, unless test is wrong) |
Timeout on async test |
Missing await or slow operation | Add proper await, increase timeout, or mock slow calls |
ECONNREFUSED in test |
Test needs running service | Use service containers or mock the dependency |
| Snapshot mismatch | Intentional UI change | Update snapshots with --update-snapshot if change is correct |
| Different results on CI vs local | Environment difference | Check timezone, locale, file ordering, parallelism |
Category 4: Timeout Failures
Signals:
The job running on runner ... has exceeded the maximum execution time- Step shows no output for extended period then dies
##[error]The operation was canceled
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
| Job timeout (6h default) | Infinite loop or hung process | Set explicit timeout-minutes per job, investigate hang |
| Step hangs with no output | Interactive prompt waiting for input | Add -y or --yes flags, pipe yes, set CI=true env |
| E2E test timeout | Browser not starting or page not loading | Check playwright install --with-deps, add retry |
| Docker build timeout | Layer cache miss on large image | Use docker/build-push-action with cache-from |
Category 5: Permission and Secret Failures
Signals:
Error: Resource not accessible by integrationError: Input required and not supplied: tokenfatal: could not read Username403 Forbiddenon API calls
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
Resource not accessible |
Insufficient GITHUB_TOKEN permissions |
Add permissions: block to workflow |
Input required: token |
Secret not available in fork PRs | Use pull_request_target or conditional step |
could not read Username |
Private repo access without token | Use actions/checkout with token: ${{ secrets.PAT }} |
403 on deployment |
Deploy key or token expired | Rotate secret in repo settings |
Category 6: Workflow Configuration Errors
Signals:
Invalid workflow filebefore any step runsUnexpected valuein YAML parsing- Steps reference nonexistent actions or wrong versions
Common causes and fixes:
| Error Pattern | Likely Cause | Fix |
|---|---|---|
Invalid workflow file |
YAML syntax error | Validate with actionlint locally |
Unexpected value 'X' |
Wrong key name or indentation | Check GitHub Actions schema for correct key names |
Unable to resolve action |
Action not found or wrong version | Verify action exists, use @v4 not @master |
Context access might be invalid |
Typo in ${{ }} expression |
Check variable names against available contexts |
Flaky Test Analysis
Flaky tests are tests that pass and fail nondeterministically. They erode trust in CI. Diagnose by pattern:
Detection
# Get last 20 runs for the test job
gh run list --workflow=tests.yml --limit 20 --json status,conclusion,startedAt
# Compare pass/fail ratio
gh run list --workflow=tests.yml --limit 50 --json conclusion | \
jq 'group_by(.conclusion) | map({conclusion: .[0].conclusion, count: length})'
If the same workflow alternates between success and failure without code changes, you have flaky tests.
Common Flaky Patterns
- Race condition: Tests share mutable state. Fix: isolate test state, use
beforeEachreset. - Timing dependency: Test assumes operation completes in N ms. Fix: use polling/waitFor instead of sleep.
- Order dependency: Test passes in isolation but fails when run after another test. Fix: ensure cleanup in afterEach.
- Port collision: Multiple test suites bind to same port. Fix: use random ports or
--shard. - Timezone/locale: Test passes in US locale but fails in CI (usually UTC). Fix: set
TZ=UTCin test env. - File system ordering:
readdirreturns different order on different OS. Fix: sort results before comparing.
Quarantine Strategy
If a flaky test cannot be fixed immediately:
- Move to a
flaky/directory or tag with.skip+ a TODO comment with issue link - Create a tracking issue with reproduction details
- Set a deadline (2 weeks max) for resolution
- Never delete a flaky test -- it caught a real issue at least once
Workflow Validation
Before a workflow runs in CI, validate it locally:
# Install actionlint (the gold standard for workflow linting)
brew install actionlint # macOS
# or: go install github.com/rhysd/actionlint/cmd/actionlint@latest
# Lint all workflow files
actionlint .github/workflows/*.yml
# Check a specific file with verbose output
actionlint -verbose .github/workflows/ci.yml
Common YAML Pitfalls
- Unquoted
on:triggers:on: pushworks, buton: [push, pull_request]needs the brackets - Missing
runs-on: Every job needs it - Wrong indentation: Steps under
steps:must be at the right level - Expression syntax:
${{ github.event.pull_request.number }}not${ github.event... } - Matrix include/exclude:
includeadds combinations, it does not filter -- useexcludeto remove
Anti-Patterns
1. Retry Until Green
Symptom: Re-running failed workflow until it passes Why wrong: Masks real issues, wastes compute minutes, teaches team to ignore CI Fix: Investigate every failure. If genuinely flaky, quarantine the test and fix the root cause.
2. Disabling Failing Checks
Symptom: Removing status checks from branch protection because they fail
Why wrong: Opens main branch to broken code
Fix: Fix the check, or mark it as continue-on-error: true with a tracking issue.
3. Blaming the Runner
Symptom: "CI is slow/broken, works on my machine"
Why wrong: CI environment differences are the test -- if it fails there, the code has assumptions
Fix: Match CI environment locally. Use act or Docker to reproduce. Add CI=true to local test runs.
4. Giant Log Dumps
Symptom: Pasting entire CI log into an issue or PR comment Why wrong: No one reads 10,000 lines of logs Fix: Extract the relevant 10-20 lines around the failure. Include the step name, error message, and exit code.
5. Ignoring Warnings
Symptom: CI passes but with deprecation warnings, security advisories, or linting warnings
Why wrong: Warnings become errors in the next major version. Technical debt compounds.
Fix: Treat warnings as errors in CI (--max-warnings 0 for ESLint, --strict for TypeScript).
6. Monolithic Workflow Files
Symptom: Single 500-line workflow file that does lint + test + build + deploy
Why wrong: One failure blocks everything, hard to debug, impossible to re-run selectively
Fix: Split into separate workflows or use reusable workflows with workflow_call.
Decision Points
When to escalate vs fix locally
- Fix locally: Dependency version bump, lockfile update, snapshot update, env variable typo
- Escalate: Infrastructure issue (GitHub Actions outage), permission change needed (admin-only), flaky test requiring architecture change
When a failure is infrastructure vs code
Check https://www.githubstatus.com/ first. If Actions shows degraded:
- Note the incident in the PR
- Wait and re-run
- Do NOT push empty commits to trigger reruns
If infrastructure is healthy, the failure is yours to own.
When matrix failures are partial
If only node-18, ubuntu-latest fails but node-20, ubuntu-latest passes:
- The fix is probably a Node 18 compatibility issue
- Check if the project's
engines.nodematches the matrix - Consider dropping the failing version if it is EOL
Quality Checklist
[ ] Identified the exact failing step (not just "CI failed")
[ ] Classified failure into correct category (dependency/build/test/timeout/permission/config)
[ ] Reproduced locally when possible (or explained why not)
[ ] Fix addresses root cause, not symptoms (no blind retries)
[ ] Flaky tests either fixed or quarantined with tracking issue
[ ] Workflow YAML validates with actionlint after changes
[ ] CI minutes impact considered (larger runner = more cost)
[ ] Secrets verified as available for the trigger type (push vs pull_request vs fork)
[ ] Timeout values set explicitly on long-running jobs
[ ] Status check passes on the fix PR before merging
Quick Reference Commands
# List recent workflow runs
gh run list --limit 10
# View a specific run (shows jobs and steps)
gh run view <run-id>
# Get only failed step logs
gh run view <run-id> --log-failed
# Re-run failed jobs only (not the whole workflow)
gh run rerun <run-id> --failed
# Watch a running workflow
gh run watch <run-id>
# List workflow files
ls .github/workflows/
# Validate workflow syntax locally
actionlint .github/workflows/*.yml
# Check GitHub Actions service status
open https://www.githubstatus.com/
# Run workflow manually (if workflow_dispatch enabled)
gh workflow run <workflow-name>
Covers: GitHub Actions diagnosis | Failure classification | Flaky test analysis | Workflow validation | Permission debugging | Timeout investigation
Use with: github-actions-pipeline-builder (create workflows) | ci-cache-optimizer (fix cache issues) | test-automation-expert (fix failing tests) | site-reliability-engineer (production CI/CD)