Events, Triggers, Filters, Schedules, Manual Runs, and Repository Dispatch: Diagnostics, Failure Modes, and Production Practices
A trigger failure is often misdiagnosed because people look only at YAML and the latest Actions page. This lesson uses a causal sequence: preserve what happened, determine whether an event existed, identify the selected ref/SHA and workflow revision, prove filter evaluation, and only then inspect jobs/runners if a run was actually created.
Learning objectives
- Diagnose “no run,” “wrong run,” and “run but skipped job” as separate failure classes.
- Explain why branch and path filters combine with AND semantics when both are configured.
- Diagnose wrong pull-request SHA assumptions using merge-ref, head-ref, and payload evidence.
- Recognize schedule delay/default-branch behavior without treating it as runner failure.
- Repair an intentionally broken trigger with the smallest change while preserving first-failure evidence.
1. Evidence-first trigger diagnostic sequence
- Preserve: event/commit/PR/API request evidence and any existing run ID/attempt.
- Confirm event: did the repository activity or API request actually happen?
- Confirm workflow eligibility: did the workflow exist at the required ref/default branch?
- Evaluate filters: activity types, branches/tags, paths, commit-message skip rules.
- Confirm ref/SHA: especially for PR merge context versus head context.
- Only if a run exists: inspect job conditions, queue, runner, steps, permissions, external systems.
- Repair minimally: change one causal input, repeat the smallest equivalent event, compare evidence.
2. Failure class A — expected a run, but no run exists
Start with a prediction table, not a YAML rewrite:
| Question | Evidence | Common cause |
|---|---|---|
| Did the event occur? | Commit/PR/API response/audit history | Wrong repository, branch, or client call |
| Was workflow eligible? | Workflow path on associated/default branch | Manual/scheduled/custom workflow exists only on feature branch |
| Did branch/tag filter match? | Event ref/base branch + glob rules | PR filter evaluated against target branch, not head branch |
| Did path filter match? | Changed paths + workflow filter | Only docs changed while workflow watches app paths |
| Was workflow disabled/skipped? | Actions settings; commit skip annotation | Disabled workflow or explicit skip condition |
3. Intentionally broken example — assuming branch OR path
on:
push:
branches:
- main
paths:
- 'app/**'
A learner pushes docs/notes.md to main and
expects the branch match alone to create a run. It does not. When
both filters are defined, both must be satisfied. Preserve the
commit SHA and absence of a workflow run. Do not “fix” it by
changing runs-on, permissions, or shell because no job
was ever queued.
Two valid repairs answer different policy questions:
-
remove the path filter if every
mainpush should create a run; - keep the filter if docs-only changes genuinely make the workflow irrelevant.
4. Failure class B — run exists, but the revision is not the one you assumed
The classic example is pull_request. A workflow
compares GITHUB_SHA with the contributor's last commit
and declares “wrong checkout.” But the event intentionally exposes
the PR merge commit as GITHUB_SHA. Diagnose by
recording both:
env:
EVENT_SHA: ${{ github.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
REF: ${{ github.ref }}
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
steps:
- run: |
printf 'event_sha=%s pr_head_sha=%s\n' "$EVENT_SHA" "$PR_HEAD_SHA"
printf 'ref=%s base=%s head=%s\n' "$REF" "$BASE_REF" "$HEAD_REF"
Repair the expectation first. Only change checkout behavior if the workflow's actual requirement is to test the head commit rather than the merge result.
5. Failure class C — cron did not start at the expected second
Do not interpret schedule delay as a syntax or runner failure until you inspect current schedule rules. Scheduled workflows run from the default branch, have a minimum five-minute interval, and can be delayed during high Actions load. Current docs specifically note that the start of an hour is a high-load period and suggest choosing a different minute when possible.
6. Failure class D — “fixing” permissions by choosing a more privileged trigger
A fork PR cannot access the same secrets/write capabilities as
trusted branch automation, and that is intentional. Switching from
pull_request to pull_request_target and
then checking out/executing untrusted PR code is not a routine
permissions fix. It moves execution into a trusted base context and
can create a severe privilege boundary violation.
In Chapter 03, the safe repair is to keep the untrusted validation job read-only. If privileged follow-up is required, separate it behind an explicit trusted control plane and defer the full design to Chapter 22.
7. Failure class E — expecting every
GITHUB_TOKEN mutation to trigger another workflow
GitHub deliberately suppresses most workflow-triggering recursion
for events caused by the repository's GITHUB_TOKEN.
Current behavior makes exceptions for
workflow_dispatch and repository_dispatch,
and has special approval behavior for certain PR events. If a
workflow writes to the repository or an issue and you expected
another workflow to cascade automatically, inspect token/event
recursion rules before adding retries.
8. Broken trigger mini-lab
Create this disposable workflow on main:
name: Broken trigger lab
on:
push:
branches: [main]
paths: ['app/**']
permissions: {}
jobs:
prove:
runs-on: ubuntu-24.04
steps:
- run: echo "created for $GITHUB_REF at $GITHUB_SHA"
-
Commit a docs-only change on
main. Predict no run; record commit SHA and absence. - Before editing YAML, explain AND semantics from the current workflow.
-
Commit an
app/**change. Predict a run; record run ID/attempt/ref/SHA. - If your policy wanted docs-only visibility, repair by removing the path filter or moving selective logic to job conditions. Preserve the original evidence.
9. Diagnostic anti-patterns
- Editing
runs-onwhen no run exists. -
Adding
write-allpermissions to “make triggers work.” - Dumping the entire event context to logs instead of selecting necessary fields.
-
Replacing
pull_requestwith a privileged trigger to regain secrets. - Assuming a schedule must fire exactly at the cron second and repeatedly editing cron without preserving actual timestamps.
- Deleting failed or surprising runs before recording run IDs, attempts, refs, and payload evidence.
Knowledge check
A docs-only push to main does not create a run for
a workflow with branches: [main] and
paths: [app/**]. What layer failed?
No layer failed. The trigger contract did not match because branch and path filters both must match; no runner diagnosis is appropriate.
A PR run uses a SHA different from the head commit. What should you inspect before changing checkout?
Inspect github.ref, github.sha, and
github.event.pull_request.head.sha; normal PR CI
uses the merge-ref/merge SHA by design.
Why is a delayed scheduled run not automatically a GitHub-hosted runner problem?
Schedule eligibility and service delay occur before or independently of runner execution; GitHub documents that scheduled runs can be delayed under load.
Why is pull_request_target not a normal fix for
missing fork secrets?
It changes the trust boundary to the base repository context; executing untrusted PR code there can expose privileged credentials or mutation capabilities.
A workflow uses GITHUB_TOKEN to create an issue
and expects another workflow to trigger on that issue. What
should you verify?
Verify GitHub's recursion-suppression rules for events caused by
GITHUB_TOKEN; most such events do not start new
workflow runs.
Official references and version notes
- Events that trigger workflows — authoritative event-specific ref/SHA, default-branch, activity-type, schedule, and dispatch semantics.
- Triggering a workflow — branch/path filters, manual inputs, event configuration, and trigger examples.
-
Workflow syntax for GitHub Actions
— current
onsyntax, filter combination rules, schedule syntax, and manual input types. -
Contexts reference
— current
githubcontext fields and safe access patterns. -
Variables reference
— current meanings of
GITHUB_REF,GITHUB_SHA, event path, and run identity variables. - Troubleshooting workflows — current guidance for trigger conditions, default-branch requirements, merge-conflict behavior, and schedule delays.
- GITHUB_TOKEN concepts — current recursion suppression and exceptions for dispatch events.
- Secure use reference — privileged trigger and untrusted-input security guidance.
Version-sensitive trigger behavior was rechecked against current
primary GitHub documentation on 2026-09-09.
Executable labs use a disposable repository,
ubuntu-24.04, built-in shell steps only, and explicit
permissions: {} because no repository API mutation is
required from inside the workflow. At verification time,
workflow_dispatch, repository_dispatch,
and schedule require the workflow file to exist on
the default branch; scheduled workflows run from the latest
default-branch commit, support POSIX cron plus an optional IANA
timezone, and have a minimum five-minute interval but may be
delayed under load. GitHub is continuously delivered, so event
payload fields, limits, schedule behavior, and default-branch
rules must be rechecked before long-lived production use. The
deliberately broken examples remain side-effect free: they create
only disposable commits and Actions run history. The correct
response to an upstream trigger mismatch is not broader token
scope or a more privileged event.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.