1. Introduction
You push a commit, open the Actions tab, and… nothing. No failed run, no queued run — the workflow simply never triggered. This is more frustrating than a red build because there's no log to read. The workflow either wasn't eligible to run, GitHub couldn't parse it, or the event you expected doesn't match what actually happened.
Ninety percent of "workflow not triggering" cases come down to a handful of causes: the on: event/filters don't match the push, the workflow file isn't on the branch you pushed to, a YAML error makes the file invalid, the workflow is disabled, or fork/permission rules blocked it. This guide gives you a fast triage path and the fix for each.
2. How Triggering Actually Works
GitHub evaluates a workflow's on: block against the event that just occurred. For it to run, three things must all be true:
- The workflow file exists at
.github/workflows/*.ymlon the ref the event targets (forpush, that's the branch you pushed) - The file is valid YAML and a valid workflow schema
- The event and its filters (
branches,paths,tags,types) match
3. Common Causes
on:filters exclude your event — e.g.branches,paths, orpaths-ignorefilter it out- The workflow YAML is invalid, so GitHub skips it silently (a banner appears on the Actions tab)
- The workflow file isn't on the target branch yet (added on a different branch)
- Actions are disabled for the repo, or the specific workflow was disabled in the UI/API
- Default
GITHUB_TOKENpushes don't trigger new workflow runs (prevents recursion) - Fork pull requests:
pull_requestfrom a fork has restricted permissions and needs approval; secrets aren't available - Using
on: pushtags without a matchingtags:filter, or an over-broadpaths-ignore - Repository or org policy restricts which actions/workflows may run
4. Step-by-Step Diagnosis and Fix
Step 1: Check the Actions tab for a parse-error banner
An invalid workflow doesn't show as a failed run — GitHub refuses to schedule it and shows an "invalid workflow file" banner. Validate before anything else.
# Lint locally with actionlint (catches schema + expression errors)
actionlint .github/workflows/ci.yml
# Or validate YAML shape quickly
python -c "import yaml,sys; yaml.safe_load(open('.github/workflows/ci.yml'))"
# Confirm the file is even tracked on this branch
git ls-files .github/workflows/
Step 2: Confirm the file is on the branch you pushed
# What branch am I on, and does it contain the workflow?
git branch --show-current
git show HEAD:.github/workflows/ci.yml >/dev/null && echo "present on this commit"
# Remember: push events read the workflow FROM the pushed branch.
# Editing on feature/x won't affect what runs when you push main.
Step 3: Match the event and filters precisely
# A push to a feature branch will NOT trigger this:
on:
push:
branches: [main] # only main triggers on push
# To run on PRs into main from any branch, use pull_request:
on:
pull_request:
branches: [main]
# Path filters silently skip runs when no matching file changed:
on:
push:
paths:
- "src/**" # push touching only README.md -> no run
branches: [main]
Step 4: Check that Actions and the workflow are enabled
# Is Actions enabled for the repo?
gh api repos/OWNER/REPO/actions/permissions
# List workflows and their state (active vs disabled_manually)
gh workflow list --all
# Re-enable a manually disabled workflow
gh workflow enable ci.yml
Step 5: Handle the GITHUB_TOKEN recursion rule
Events triggered by the default GITHUB_TOKEN (e.g. a bot commit pushed by another workflow) do not start new workflow runs — this is deliberate loop protection. If you need a downstream workflow to fire, push with a PAT or a GitHub App token, or use workflow_dispatch/repository_dispatch.
# Manually trigger a dispatch-enabled workflow to confirm it works at all
gh workflow run ci.yml --ref main
# Requires the workflow to declare:
on:
workflow_dispatch: {}
Step 6: Fix fork pull_request restrictions
# PRs from forks run with read-only GITHUB_TOKEN and no secrets, and may
# require maintainer approval before running. For workflows that must
# access secrets on PR, use pull_request_target CAREFULLY (it runs in the
# base repo context) and never checkout+run untrusted PR code with secrets.
on:
pull_request_target:
types: [opened, synchronize]
5. Verification Steps
# Trigger a real push and watch runs appear from the CLI
git commit --allow-empty -m "ci: trigger test" && git push
gh run list --workflow ci.yml --limit 5
# Watch the latest run live
gh run watch
# Inspect why a run did/didn't start (event + head_branch)
gh run list --json databaseId,headBranch,event,status --limit 5
6. Common Mistakes
- Editing the workflow on a feature branch and expecting a
push: [main]filter to run it - Assuming a YAML error will show as a failed run — it shows as an "invalid workflow" banner instead
- Over-broad
paths-ignorethat silently skips the runs you wanted - Expecting a bot commit made with
GITHUB_TOKENto trigger another workflow - Debugging secrets on a fork PR — forks get no secrets and a read-only token by design
7. Prevention Tips
- Run
actionlintin a pre-commit hook or a dedicated lint job so invalid workflows never merge - Add a
workflow_dispatchtrigger to every workflow so you can manually test triggering - Be explicit with
branches/pathsfilters and comment why each exists - Prefer a PAT/App token when one workflow must intentionally trigger another
- When the workflow triggers but fails, switch to debugging a failing pipeline and check env vars & secrets
8. FAQ
I pushed a new workflow file but it didn't run. Why?
For a push event, GitHub reads the workflow from the branch you pushed. If you added the file on a feature branch but the on: push: branches filter only lists main, nothing runs. Merge to (or push) the branch the filter allows, and make sure the file is committed there. Also check the Actions tab for an "invalid workflow file" banner.
My workflow creates a commit, but the next workflow never triggers.
By design, events raised by the default GITHUB_TOKEN do not start new workflow runs — this prevents infinite loops. Push the commit using a Personal Access Token or a GitHub App installation token, or trigger the downstream workflow explicitly via repository_dispatch/workflow_dispatch.
Workflows run on my branches but not on pull requests from forks.
Fork PRs run with a read-only GITHUB_TOKEN, have no access to secrets, and often require a maintainer to approve the run. That's a security boundary, not a bug. If a workflow genuinely needs elevated context on PRs, use pull_request_target carefully and never execute untrusted PR code while secrets are in scope.
9. Summary
| Symptom | Cause | Fix |
|---|---|---|
| No run at all | Event/branch filter mismatch | Align on: with the actual event/branch |
| "Invalid workflow file" banner | YAML/schema error | Lint with actionlint, fix syntax |
| Docs commit skipped | paths / paths-ignore filter | Adjust path filters |
| Downstream workflow silent | GITHUB_TOKEN recursion rule | Push with PAT/App token |
| Fork PR won't run | Fork permission model | Approve run; rethink secret use |
Explore More in This Category
Explore more in this category: CI/CD guides. Browse all DevOps Compass articles or jump to: Kubernetes, AWS, CI/CD, Containers, Monitoring, Networking.