DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk8 min

Your GitHub Actions Workflow Says One Thing. Its Execution Paths Say Another.

A GitHub Actions workflow file describes intent, but each run passes through five layers that decide what actually executes. Here is how to check them in order.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

GitHub runs the YAML you wrote. The difference between what a file appears to say and what a run does comes from the stages a run passes through after the file is read. Each run is shaped by five layers: the trigger and its filters, the moment each expression is evaluated, the job dependency graph, the boundary between a caller and a reusable workflow, and the execution and security policies that apply to the repository. If two runs of the same workflow behave differently, or a run does something the file doesn’t seem to call for, check those layers in order against that run’s own details.

Why the file and the run can disagree

A workflow is a YAML-defined automated process made up of one or more jobs. Events trigger it: activity on GitHub, a schedule, or an external event. The GitHub Docs reference for workflows and actions describes this model. What the file cannot show on its own is which steps will execute in a particular run. That depends on three things the file does not contain: the values an expression sees at the moment it is evaluated, the state of other jobs at that moment, and the settings of the repository, organization, or enterprise the run belongs to.

As an Amazon Associate I earn from qualifying purchases.

The five layers, in the order a run passes through them

Layer Question it answers Where to check
1. Trigger and filters Was a run requested for this event, branch, and path? The on block, branch and path filters, and the event recorded for the run
2. Expression evaluation Which values did each if condition see, and at what stage? Job-level and step-level if expressions and the contexts they read
3. Job dependency graph Did needs allow the job to start, and what were the upstream results? jobs.<job_id>.needs, upstream job results, and job-level conditions
4. Reusable workflow boundary What did a called workflow inherit from its caller? The uses reference, inputs, secrets, permissions, and the Actions access settings of both repositories
5. Execution and security policy Did an administrative policy or trust rule allow this actor and event to execute? Enterprise, organization, and repository Actions policies, and how pull_request_target is used

Layer 1: Triggers and filters decide whether a run starts

The trigger decides when a workflow run is requested. Before you look at any job, confirm which event started the run and whether your filters match that event. A branch filter is matched against the ref the event carried, so a run created from a feature branch will not match a filter written for main. Path filters depend on which files the triggering event changed, so a commit that touches only documentation may not start a workflow whose path filter lists source directories.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A rerun is another attempt of the same run. When you compare two results, read the run attempt number first; otherwise you may be comparing a later attempt against the original without realizing it.

Layer 2: Expressions are evaluated at different stages

Contexts expose information about the workflow run, variables, the runner environment, jobs, and steps, but they are not all available at every point. GitHub’s Contexts documentation states the key rule for conditions: “The if check is processed by GitHub Actions, and the job is only sent to the runner if the result is true.” A job-level condition is therefore decided before any runner exists, which explains many jobs that were skipped even though a condition looked correct in the file.

Default environment variables are the clearest example of a value that is not there yet. They exist on the runner, so a job-level condition cannot depend on them. A step-level condition runs on the runner, after the job has started, so it can. The pattern below shows both stages in one job:

jobs:
  deploy:
    # Job-level: decided before the job is sent to a runner
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Check target
        # Step-level: evaluated on the runner, after the job has started
        if: env.DEPLOY_TARGET == 'prod'
        run: echo "deploying"

When a value looks wrong, compare it against the stage where the check runs, not against what a later step printed. The Expressions documentation covers the syntax and operators these conditions use.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Layer 3: needs turns a job list into a dependency graph

Jobs run in parallel unless jobs.<job_id>.needs says otherwise. A job with needs waits for the jobs it names. GitHub documents that when a dependency fails or is skipped, the dependent job is skipped as well, unless a conditional expression causes it to continue. In practice:

  • With no custom condition, a dependent job runs only when every job it depends on succeeded.
  • If an upstream job failed, the downstream job is skipped by default.
  • If an upstream job was itself skipped, the downstream job is skipped by default too, so a skip can cascade through a long chain.
  • always() is the documented way to run a job despite a failed dependency. When you use it, check each dependency’s result (for example, needs.<job_id>.result) so the job does not act on a failed upstream step.

A skipped downstream job often looks like a trigger or condition problem. Check the upstream results first.

Layer 4: Reusable workflows change who owns what

A reusable workflow is called from a caller workflow with uses. The called workflow is not simply a copy of the caller’s YAML placed in the same run. The boundary between the two changes several things:

jobs:
  call-build:
    uses: my-org/shared-workflows/.github/workflows/build.yml@3f9c2a1b7d4e6f80a1b2c3d4e5f60718293a4b5c
    with:
      node-version: "20"

What the caller controls and what the callee inherits

  • The github context in the called workflow is associated with the caller.
  • Hosted runner assignment and billing are associated with the caller.
  • Values in the caller’s workflow-level env block do not propagate automatically into the called workflow. Pass what the callee needs as inputs.
  • Reusable workflow outputs are the documented way to return data from the callee to the caller.
  • GITHUB_TOKEN permissions can be kept or reduced through a nested call. They cannot be elevated down the chain.

If a called job sees a different repository, runner, variable, or permission level than its caller, the boundary is the first place to look.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Access settings that can stop a call

The caller’s Actions settings must allow the use of actions and reusable workflows. A called workflow in a private repository also needs an access policy that permits the caller. The reusing workflow configurations reference sets out these conditions. When a call that worked earlier stops starting, compare these settings in both repositories before you change the YAML.

Nesting limits and reference pinning

GitHub documents two product limits for reusable workflows: a maximum nesting depth of ten levels, and a maximum of fifty unique reusable workflows called from one workflow file. Treat these as platform limits, not as properties of your code.

Pinning matters for reproducibility. When the reference is not a full commit SHA, reruns can resolve the called workflow differently depending on whether you rerun all jobs or only failed or specific jobs. Pinning to a full commit SHA, as in the example above, keeps the called code fixed across attempts. The reuse documentation covers the rerun behavior for each case, so check it before relying on a branch or tag reference in a workflow you need to reproduce.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Layer 5: Execution policy and the pull_request_target boundary

Administrative policies can block a valid workflow

GitHub Actions execution protections can restrict which actors and events are allowed to run workflows at the enterprise, organization, or repository level. The events affected include push, pull_request, pull_request_target, and workflow_dispatch. A workflow file can be syntactically correct and still produce no run because policy blocks it. The About Actions policies page and the guide to controlling who can execute GitHub Actions workflows describe the scopes. Check each scope the repository sits under.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why pull_request_target is a trust boundary

GitHub’s guidance is direct: “Only allow pull_request_target when it is necessary.” (GitHub Docs, Securely using pull_request_target.) The event runs with access to repository secrets and a privileged GITHUB_TOKEN, which is why the risk is about what code the workflow executes, not only what it says.

Do not check out, build, or execute untrusted pull-request code in this kind of workflow while secrets or a privileged token are available. The risk is not limited to obviously dangerous commands. Build commands, package installation, dependencies, and configuration files can all execute contributor-controlled code. Prefer pull_request when the job does not need secret access. When a workflow needs both untrusted code and privileged operations, separate the two into different jobs or workflows.

The November 2, 2026 default block

As of October 2026, GitHub’s documentation describes a default policy that blocks pull_request_target in affected public repositories. The policy is described as being in evaluate mode, with enforcement scheduled for November 2, 2026, which is less than four weeks after this article was published. Three limits apply:

  • It applies to affected public repositories. It does not apply to private or internal repositories.
  • It does not replace an applicable policy already configured for the repository. If one exists, that policy governs.
  • Because of the second point, check the repository’s current policy state before you assume a specific run will be blocked.

Where to start when a run disagrees with the file

Start with the symptom, then go to the layer that most often produces it:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • The run never started, or started on a branch you did not expect: Layer 1, trigger and filters.
  • A job was skipped although its condition looks true: Layer 2 for the evaluation stage, then Layer 3 for needs.
  • A downstream job was skipped after an upstream failure or skip: Layer 3.
  • A called workflow sees different values, runner, or permissions than its caller: Layer 4.
  • A valid workflow produced no run at all: Layer 5, policy and trust settings.

Comparing two runs that should match

When two runs of the same workflow differ, compare the same inputs for each run rather than reading the logs side by side:

  1. Record each run’s event, ref, run attempt number, and the commit SHA of the workflow file. Two runs with different file commits were not testing the same YAML.
  2. Compare each run’s event payload against the trigger and filters in the file.
  3. For every job and step with an if, note the values it saw at the stage where it was evaluated. Job-level values come from before the job is routed to a runner; step-level values come from the runner.
  4. For every skipped or failed job, map its needs list and each upstream result.
  5. For each reusable workflow call, compare the reference, inputs, secrets passed, permissions, and the Actions access settings of both repositories.
  6. Check policy at the enterprise, organization, and repository scopes. For pull_request_target, also check the repository’s policy state against the November 2, 2026 date above.

The documentation explains how each mechanism works. It cannot tell you what a particular repository’s settings were when a particular run executed. For that, use the run’s own log and event details, along with the settings that were in force at that time.

The Bottom Line

Bottom line: The YAML is read as written. What changes is when each part is evaluated, what the job graph and reusable-workflow boundary allow, and which policies apply. Start with the layer that matches the symptom, then confirm against the run’s own event, attempt number, and settings from the time it ran.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.