GitHub Actions Tutorial — Debugging Strategies
Debugging Strategies
When a workflow fails and the error isn’t obvious, follow a systematic approach. This page covers practical strategies for isolating and resolving issues.
The Debugging Checklist
1. Read the error message (carefully)
2. Check which step failed
3. Enable debug logging
4. Reproduce locally with act
5. Isolate the failing step
6. Check environment differences
7. Verify secrets and permissions
Strategy 1 — Print Environment
When you’re not sure what’s available on the runner, dump the environment:
steps:
- name: Debug environment
run: |
echo "=== OS ==="
uname -a
echo "=== Working directory ==="
pwd
echo "=== Files ==="
ls -la
echo "=== Environment ==="
env | sort
echo "=== GitHub context ==="
echo "Event: ${{ github.event_name }}"
echo "Ref: ${{ github.ref }}"
echo "SHA: ${{ github.sha }}"
Strategy 2 — Dump Contexts
Print the full GitHub context objects:
steps:
- name: Dump contexts
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
JOB_CONTEXT: ${{ toJSON(job) }}
STEPS_CONTEXT: ${{ toJSON(steps) }}
RUNNER_CONTEXT: ${{ toJSON(runner) }}
run: |
echo "=== GitHub ===" && echo "$GITHUB_CONTEXT"
echo "=== Job ===" && echo "$JOB_CONTEXT"
echo "=== Steps ===" && echo "$STEPS_CONTEXT"
echo "=== Runner ===" && echo "$RUNNER_CONTEXT"
Warning: Don’t dump secrets context — it would expose secrets in logs.
Strategy 3 — Isolate with continue-on-error
Keep the workflow running past the failing step to gather more information:
steps:
- name: Failing step
id: might-fail
continue-on-error: true
run: npm test
- name: Debug after failure
if: steps.might-fail.outcome == 'failure'
run: |
echo "Step failed — gathering diagnostics"
cat test-results.xml 2>/dev/null || echo "No test results file"
ls -la node_modules/.cache/ 2>/dev/null || echo "No cache"
Strategy 4 — SSH into the Runner
Use tmate to get an interactive SSH session on the runner:
steps:
- uses: actions/checkout@v4
- name: Setup tmate session
if: failure() # only on failure
uses: mxschmitt/action-tmate@v3
timeout-minutes: 15
The step output shows an SSH command:
SSH: ssh xxxxx@nyc1.tmate.io
Connect and debug interactively. The session times out after 15 minutes.
Strategy 5 — Minimal Reproduction
Create a minimal workflow that reproduces the issue:
name: Debug
on: workflow_dispatch
jobs:
debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Add only the failing step
- run: |
echo "Testing the specific command..."
# paste the failing command here
Trigger with workflow_dispatch to test on demand without polluting your main branch.
Strategy 6 — Check Runner Differences
Your local machine isn’t the runner. Common differences:
| Factor | Local | Runner |
|---|---|---|
| OS version | macOS 14 | Ubuntu 22.04 |
| Shell | zsh | bash |
| Env vars | Custom | GitHub-provided |
| Permissions | Your user | runner user |
| Network | Home/office | Azure data center |
| Installed tools | Varies | Runner image |
Check what’s installed
steps:
- run: |
which node && node --version
which python3 && python3 --version
which docker && docker --version
which java && java -version 2>&1
which go && go version
Strategy 7 — Binary Search for Failures
If a long script fails, binary-search for the failing line:
steps:
- run: |
set -x # print every command before execution
command_1
command_2
command_3 # if this fails, you see it immediately
command_4
set -x (bash debug mode) prints each command before execution — you can see exactly which line failed.
Common Mistakes
| Mistake | Fix |
|---|---|
| Debugging in main branch | Use workflow_dispatch for debug workflows |
| tmate left running | Always set timeout-minutes |
| Dumping secrets context | Never — use env | grep instead |
| Not checking runner image | Compare local vs runner tool versions |
Quick Reference
| Strategy | When to Use |
|---|---|
| Print environment | “Works locally but not in CI” |
| Dump contexts | Wrong trigger, wrong values |
| continue-on-error | Need post-failure diagnostics |
| tmate SSH | Need interactive debugging |
| Minimal workflow | Complex workflow, unclear cause |
| Runner differences | Tool version mismatches |
set -x |
Long script, unclear failure point |
← Previous: Reading Logs
Next: Practice Exercises →