GitHub Actions skipped job required check, and what blocks the merge
A GitHub Actions skipped job required check reports its status as Success, so a job that an if condition skipped is not what is holding your pull request open. What holds it open is a check that was never created at all, which is a different situation with a different fix.

What this error means
The pull request sits with a merge button you cannot press and a checks box that never settles. One entry reads as skipped and looks like the culprit. Another entry, the one that is actually required, has no run behind it: no duration, no logs, no link to a job, because no workflow run ever produced it. Nothing in the Actions tab is red. If you push straight at the protected branch instead, git refuses with the message below, which names the check but not the reason.
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Required status check "ci-build" is failingA workflow that produces both states at once
This file is written for this page and has never been run. It is the smallest shape that puts a skipped job and an uncreated check side by side in the same pull request, so you can tell which one you are looking at. The workflow only runs when a pull request touches something under scripts. Inside it, the deploy job is skipped on every pull request by its own condition.
Require build in a ruleset and a pull request that touches only the README will wait forever, because this workflow never started and never reported build. Require deploy instead and nothing waits, because deploy is skipped and skipped reports Success. Same file, same pull request, opposite outcomes.
name: ci
on:
pull_request:
paths:
- 'scripts/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- run: ./scripts/build.sh
deploy:
needs: build
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- run: ./scripts/deploy.shCommon causes
The required check was never created
By a wide margin the commonest one. A workflow filtered out by paths, branches or a skip instruction in the commit message does not run, so the jobs inside it never report. The troubleshooting page states the consequence in its own words: Associated checks stay in a "Pending" state and block merging. Nothing failed, so nothing in the Actions tab looks wrong.
The run happened on an event whose checks are not evaluated
A workflow started by workflow_dispatch, schedule or workflow_run can pass on the head commit and still leave the required check unsatisfied, because only six trigger events produce job checks that a pull request reads. This is the one that survives every edit to the job condition, because the condition was never the subject.
The required context name no longer matches a job
Branch protection stores the context as a string. Rename the job, add a matrix that suffixes the check name, or move the job into a reusable workflow whose check name is composed differently, and the stored name matches nothing. The old name keeps waiting for a report that no job will ever send under that name.
The job really is skipped, and that is not your blocker
A job skipped by its own if reports Success and is explicitly documented as not preventing a merge. It is worth listing last because it is the first thing everyone checks, and because time spent rewriting that condition is time not spent on the three causes above.
How to fix it
Click the entry that is not settling
- Open the checks box on the pull request and find the required context by name, not by whichever entry looks unusual.
- If clicking it takes you to a job, the workflow ran: the problem is the result, the commit, or the name.
- If there is nothing to click, no run produced it: the problem is the trigger, a path filter or a branch filter.
- Compare the context name in the ruleset against the job name in the run, character for character.
Move the filter off the workflow and into the jobs
A required workflow should always run and decide internally what work to do. Filtering inside a job keeps the check reporting, because a skipped job reports Success while a skipped workflow reports nothing. This is the same trade the troubleshooting page recommends when it advises avoiding requirements on workflows that can be skipped.
name: ci
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
scripts:
- 'scripts/**'
- if: steps.changes.outputs.scripts == 'true'
run: ./scripts/build.shRequire a gate job rather than the work job
Point the ruleset at a job that is never conditional and that inspects the results it depends on. Use always() so the gate still runs when an upstream job was skipped or failed, and read needs.<job>.result rather than assuming success. Keep the gate in the same workflow file as the jobs it reads, because needs does not reach across files.
gate:
needs: [build]
if: always()
runs-on: ubuntu-latest
steps:
- run: '[ "${{ needs.build.result }}" != "failure" ] || exit 1'Change the trigger rather than the condition
If the run that produced your green checks was started by hand or on a schedule, no edit inside the workflow will make those checks count. Add pull_request to the on block so the same jobs also run on an eligible event, and let the manual trigger stay for the cases it exists for.
on:
workflow_dispatch:
pull_request:
branches: [main]Skipped is a passing state, and the docs say so twice
The conditions how-to settles the argument in a note of its own. Quoting it in full: A job that is skipped will report its status as "Success". It will not prevent a pull request from merging, even if it is a required check. The same note is reused on the status checks reference, so this is not an aside in one page.
The status checks reference then defines the conclusion itself: "skipped" is described as "The check run was skipped. This is treated as a success for dependent checks in GitHub Actions." And the protected branches page lists the accepted set outright: "Required status checks must have a successful, skipped, or neutral status before collaborators can make changes to a protected branch."
So the skipped entry in your checks box is doing nothing to you. The Actions UI even labels it: the same how-to records that skipped jobs display the message "This check was skipped." That label is what sends people down the wrong path, because it is the only visible thing in the box that looks abnormal.
What each situation actually reports
Three of these rows are the troubleshooting page's own table, published under the heading "Handling skipped but required checks". The other two come from different sections of that same page: one lists the trigger events whose job checks a pull request evaluates, and one states that a required check has to pass on the latest commit SHA, because checks from earlier commits do not satisfy it. Read the five together and the diagnosis is usually one glance.
Three of these rows block you. The tell for the first is that it has no run behind it: click the entry. If there is nothing to click, no workflow produced it, and no amount of editing the job condition will change that.
| Situation | What the required check reports | Blocks the merge |
|---|---|---|
Workflow skipped by paths, branches or a commit message | stays "Pending", no run behind it | yes |
| Workflow ran on an event that is not evaluated for the pull request | no check appears at all | yes |
| Job skipped by a conditional | "Success" | no |
Job skipped because a job it needs failed | skipped, "may not block merging" | no |
| Check ran on an earlier commit only | not counted for the latest SHA | yes |
The gate job, and the case where it is the wrong answer
The usual fix is a small job that is always required and that decides for itself whether the thing it guards was allowed to be absent. It works because the gate job is not conditional, so it always produces a check, and because it reads the result of the job it depends on rather than assuming one.
It is the wrong answer when the required check is missing because the workflow never ran. A gate job inside a workflow that path filtering skipped is skipped along with everything else in that file, so it reports nothing and the pull request stays blocked exactly as before. For that case the fix is on the workflow, not inside it: drop the paths filter from the required workflow and filter inside the jobs, or stop requiring a workflow that can be filtered out.
gate:
needs: [build, deploy]
if: always()
runs-on: ubuntu-latest
steps:
- run: |
echo "build=${{ needs.build.result }} deploy=${{ needs.deploy.result }}"
[ "${{ needs.build.result }}" != 'failure' ] || exit 1
[ "${{ needs.deploy.result }}" != 'failure' ] || exit 1Why there is no recorded run on this page
There is no log to record, because the failure has no log. Everything this page is about happens on the pull request itself: a ruleset compares a list of required contexts against the check runs reported for one commit, and either finds them or does not. The observable outcome is a disabled merge button and an entry with nothing behind it, which is the absence of a run rather than the content of one. Recording a job here would produce a green log that says nothing about the merge, and a screenshot of a merge button is a picture of a setting in one repository, not evidence about how required checks behave. The states in the table above are quoted from the documentation that defines them instead.
How to prevent it
- Require a job that has no
ifof its own, so the required check is always produced. - Keep
pathsandbranchesfilters off any workflow whose jobs are required checks. - Rename a required job and update the ruleset in the same change, never separately.
- When a required check will not settle, read whether a run exists before reading what it did.
Frequently asked questions
Does a skipped job satisfy a required status check?
skipped alongside success and neutral as the conclusions that count.Why does a required check show no run and no logs?
paths or branches, or skipped through the commit message, never starts, so the check stays pending with nothing behind it. Clicking the entry and finding nothing to open is the fastest way to tell this case from a job that ran and was skipped.Can a workflow_dispatch run satisfy a required check on a pull request?
push, pull_request, pull_request_review, pull_request_target, deployment or deployment_status are evaluated for a pull request. A run started by hand on the head branch can be entirely green and still leave the requirement unmet.How do I require a job that only runs for some pull requests?
needs, uses if: always() so it is not skipped in turn, and fails only when needs.<job>.result is failure. That keeps a check reporting on every pull request while the real work stays conditional.