GitHub Actions Debugging and Troubleshooting
Workflows fail for many reasons — typos in YAML, wrong file paths, expired secrets, race conditions, and flaky tests. Knowing how to diagnose and fix problems quickly saves hours of frustration. This topic gives you a systematic toolkit for debugging any GitHub Actions workflow.
Reading Workflow Logs
The Actions tab in your repository shows the output of every workflow run. Click a run, then click a job, then expand any step to see its full log output.
Repository → Actions tab
│
└── Workflow run (click to open)
│
├── build job (click to open)
│ ├── ▶ Set up job
│ ├── ▶ Run actions/checkout@v4
│ ├── ▶ Install dependencies ← Click to expand
│ └── ▶ Run tests ← Red = failed step
│
└── deploy job (skipped)
A red X marks the step that failed. The log output beneath it shows the exact error message. Start here — most failures have a clear error message that points directly to the fix.
Enabling Debug Logging
When log output is not enough, enable debug mode to get verbose output from every step and action.
Method 1: Repository Secret
Add a secret named ACTIONS_STEP_DEBUG with the value true. All subsequent runs in that repository produce debug output.
Method 2: Re-run with debug logging
In the Actions tab, click a failed run, then click the Re-run jobs button and choose Enable debug logging. This adds debug output for that single re-run only without touching your secrets.
Debug mode reveals:
- Exact values of expressions and contexts
- Step pre/post processing details
- Tool download and installation logs
- File system state at each step
- Network calls made by actions
Adding Debug Steps to Your Workflow
Insert temporary diagnostic steps when you need to understand what the runner sees at a specific point:
steps:
- uses: actions/checkout@v4
- name: Debug — show working directory contents
run: ls -la
- name: Debug — show all environment variables
run: env | sort
- name: Debug — show git status
run: git log --oneline -5
- name: Debug — show disk space
run: df -h
- name: Debug — show current user
run: whoami && id
Remove these steps once the issue is resolved. Printing environment variables in logs can expose non-secret configuration values you might prefer to keep private.
Using tmate for Interactive Debugging
The mxschmitt/action-tmate action pauses the workflow and opens a live SSH session into the runner. You can then run commands manually to investigate the environment:
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Open SSH session for debugging
uses: mxschmitt/action-tmate@v3
if: failure() ← Only opens when a previous step fails
When the workflow hits the tmate step, it prints a connection command in the logs. You connect via your terminal, poke around the runner, and then press q in the tmate session to let the workflow continue.
Always add if: failure() so the session only opens when something goes wrong. Remove this step before merging to your main branch — leaving it active would make every failed run pause indefinitely and consume runner minutes.
Common Failures and Their Fixes
YAML Indentation Errors
Error: "You have an error in your yaml syntax"
Cause: Wrong indentation or a tab character mixed with spaces.
Fix: Use a YAML linter. Install the GitHub Actions extension
for VS Code — it highlights YAML errors as you type.
Always use 2 spaces per indent level, never tabs.
Action Not Found
Error: "Can't find 'action.yml' ... for action 'my-org/my-action'"
Cause 1: The action path is wrong.
Cause 2: The version tag does not exist.
Cause 3: The repository is private and the workflow has no access.
Fix: Verify the action name and version on GitHub Marketplace.
Check the exact tag name in the action's repository.
Secret Not Set
Error: Command fails with "unauthorized" or "permission denied"
Cause: The secret referenced in the workflow does not exist,
or is set in the wrong scope.
Fix: Go to Settings → Secrets → Actions and confirm the
secret name matches exactly (case-sensitive).
Check whether it is a repository secret or environment secret.
File Not Found After Checkout
Error: "No such file or directory: ./dist/app.js"
Cause: The file was generated in a previous job but not uploaded
as an artifact. Runner machines do not share a file system.
Fix: Upload the file as an artifact in the producing job.
Download it in the consuming job before using it.
Permission Denied on Script
Error: "Permission denied: ./deploy.sh"
Cause: The shell script is not executable on the runner.
Fix: Add this step before running the script:
- run: chmod +x ./deploy.sh
Or commit the file with execute permissions:
git add --chmod=+x deploy.sh && git commit -m "Make executable"
Cache Miss Every Run
Symptom: Cache never hits, reinstalling packages every run.
Cause 1: The cache key changes every run because the lockfile
path is wrong or the file keeps changing.
Cause 2: The path being cached does not match where packages
are actually installed.
Fix: Print the key: run: echo "${{ hashFiles('package-lock.json') }}"
Confirm the hash is stable between runs.
Verify the cache path matches the actual package directory.
Checking Workflow Syntax Before Pushing
Validate your YAML locally before pushing to avoid slow feedback loops:
Option 1: VS Code extension
Install "GitHub Actions" extension
Red underlines show errors as you type
Option 2: actionlint CLI tool
npm install -g @github/actionlint
actionlint .github/workflows/ci.yml
Option 3: GitHub's web editor
Edit the file directly on github.com/your-repo
The editor validates YAML and shows suggestions inline
Workflow Run History and Re-Runs
GitHub keeps the last 90 days of workflow run history. From the Actions tab you can:
- Re-run a failed workflow with one click (saves time — reruns only failed jobs)
- Download logs as a zip archive for offline analysis
- View which commit and branch triggered each run
- See the exact duration and cost (in minutes) of each run
Systematic Debugging Checklist
Step 1: Read the failed step log — what does the error say?
Step 2: Check environment — is the expected tool/file present?
Step 3: Check secrets — are all required secrets set correctly?
Step 4: Enable debug logging — re-run with debug enabled
Step 5: Add diagnostic steps — print env, ls, cat relevant files
Step 6: Use tmate — open an interactive shell if still stuck
Step 7: Simplify — comment out steps until the issue is isolated
Step 8: Search GitHub Issues — others have likely hit the same error
