CI/CD tools

GitHub Actions workflow validatorformat, validate, and navigate every job

Paste a workflow YAML file to format it, walk the job/step tree, and draft matrix or cron snippets before you insert them back into your workflow editor.

GitHub Actions workflow validator

Workflow YAML

Result

Everything runs in your browser. Nothing is sent to GitHub or any server.

Options
Matrix generator (draft)

Variables

include (optional)

exclude (optional)

Generate a preview to see the strategy.matrix snippet.

Cron schedule generator (draft)

Generate a preview to see the on.schedule snippet.

Waiting to validate…

A GitHub Actions workflow YAML file usually only breaks after you've already pushed it — a job missing runs-on, a permissions value that isn't in the real enum, or a schedule that quietly never fires. This GitHub Actions workflow validator catches that class of mistake locally, before a run ever starts, by checking the structure your CI actually depends on instead of only generic YAML syntax.

Reach for it when you're hand-editing a workflow with several jobs and want to jump straight to the one that's broken, adding a build matrix across operating systems and versions, wiring up env, secrets, and vars references across steps, or writing an on.schedule cron expression and need to know what GitHub Actions itself allows.

Why use this GitHub Actions workflow validator

One workbench instead of switching between a generic YAML linter, a matrix generator, and a cron tool that has never heard of GitHub Actions.

Format, validate, and navigate jobs and steps together

Paste a workflow and it gets formatted, job id and step id naming gets checked, every job is confirmed to declare runs-on or uses, the permissions enum is validated (id-token only accepts write or none), and branches/branches-ignore filters used together are flagged — then a clickable job/step tree lets you jump the editor straight to the exact line.

Formatted GitHub Actions workflow YAML next to a clickable job and step navigation tree with a structural validation error highlighted

See exactly where env, secrets, and github references will break

The reference panel lists every ${{ env.X }}, ${{ secrets.X }}, ${{ vars.X }}, and ${{ github.X }} expression it finds, with the job and step it belongs to, and flags the three documented failure spots: env can't be used in a step's id or uses, secrets can't be read directly inside an if: condition, and github can't be used inside uses:.

List of env, secrets, vars, and github expression references with three entries flagged for the exact locations GitHub Actions rejects them

A cron builder that already knows GitHub Actions' own limits

The cron panel builds a standard five-field schedule and applies the constraints GitHub documents for schedule triggers: a five-minute minimum interval, UTC by default with an optional IANA timezone, no @daily-style aliases, and the 60-day auto-disable on inactive public repositories.

Cron schedule builder showing GitHub Actions' five-minute minimum interval, UTC default, and an unsupported alias warning

Three things that are easy to miss on first use

The workbench does more automatically than the toolbar lets on.

01

Paste a workflow or load the example

Drop in workflow YAML copied from your editor, or load the built-in example — it already contains a schedule trigger, a job with a strategy.matrix, and env/secrets/github references, so you can see all five capabilities at once. Validation is on by default and re-runs 350ms after you stop typing; nothing is ever uploaded.

  • Click any job or step name in the navigation tree and the editor jumps straight to that line — the part that actually saves time once a workflow has more than a couple of jobs.
02

Treat the matrix and cron panels as drafts, not live edits

Editing the matrix variable table or the cron fields never touches your workflow by itself; it only builds a preview. Nothing is written into strategy.matrix or on.schedule until you click Insert, and until then the draft snippet can still be copied on its own.

  • Quote version numbers like node-version: "3.10" — written without quotes, YAML parses 3.10 as the number 3.1 and silently drops the trailing zero.
  • fail-fast defaults to true and max-parallel is left unset, matching what GitHub Actions itself defaults to.
03

Read the risk flags before you trust a clean result

The env/secrets/vars/github reference list marks the exact positions GitHub Actions is documented to reject — not a generic linter guess. Press Ctrl/Cmd+Enter to re-run validation when auto-run is off; if you keep editing after that, copy and download both stay disabled until the workflow is re-validated, so you never export a stale file.

  • A filter above the reference list narrows it to just env, secrets, vars, or github when a workflow has a lot of expressions.

What's actually happening under the hood

Five capabilities that work together instead of five separate tabs.

YAML formatting with your choice of indent

Reformat a workflow with consistent two- or four-space indentation, so a pull request diff only shows the lines that actually changed, not a whole file reflowed.

Structural checks against real workflow rules

Job and step ids are checked against the required naming pattern, every job is confirmed to declare either runs-on or uses, permissions values are checked against their real enum, and mutually exclusive filters like branches and branches-ignore are flagged when both appear on the same trigger.

Clickable job/step navigation tree

A tree view mirrors the workflow's real job and step structure; selecting an entry scrolls the editor to that exact line instead of making you scroll and count braces.

Matrix generator with include, exclude, and fail-fast

Build an os/version-style variable table, add include or exclude combinations, set fail-fast and max-parallel, preview the resulting strategy.matrix, and insert it into the job you choose.

env/secrets/vars/github reference inventory

Every ${{ }} expression referencing env, secrets, vars, or github is extracted and listed by job and step, with a scope filter to isolate just one context at a time.

Cron snippet generator scoped to GitHub Actions

Build a five-field schedule from common presets or a custom expression, preview the exact on.schedule entry, and insert it — GitHub's own scheduling limits are applied automatically instead of being left for you to remember.

What this checks, and what it deliberately leaves out

Knowing the edges matters more than a long feature list.

Nothing connects to GitHub
This GitHub Actions workflow validator never calls the GitHub API, never forks or pushes a repository, and never triggers or runs a workflow — it only formats and checks the text you paste, entirely on your device.
Expressions aren't evaluated
${{ github.sha }} or ${{ steps.build.outputs.version }} are extracted and listed, not resolved to a real runtime value. Testing actual expression behavior needs a dedicated expression engine, which is a separate tool — this one intentionally doesn't fake that with a JavaScript eval.
Schema checks are a focused subset, not actionlint
Structural validation covers the public schemastore.org rules that don't depend on external metadata: job/step ids, runs-on/uses, permissions, and filter conflicts. It does not check action input types, Marketplace action versions, or a live list of supported runner labels, because that metadata goes stale the moment it's cached offline.
No dependency graph
The job/step tree shows structure, not a rendered graph of needs: relationships between jobs. If you need a visual dependency diagram, that's a different kind of view this tool doesn't attempt.
Scheduling limits are documented, not guaranteed
The cron builder enforces GitHub's published rules — a five-minute minimum, UTC by default, no @daily-style aliases — but it can't promise the optional timezone field is honored, or that a schedule fires at the exact minute; GitHub notes runs can be delayed under load and disabled after 60 days of repository inactivity.

GitHub Actions workflow validator FAQ

Answers grounded in what GitHub documents, not guesses.

Does the timezone field in a GitHub Actions cron schedule actually work?

It produces a syntactically valid entry — GitHub added optional IANA timezone support to schedule in 2024 — but GitHub's own community discussions include real reports of it not being honored. This validator only guarantees the snippet matches the published syntax; it can't promise GitHub's scheduler applies it.

Why doesn't my GitHub Actions cron schedule run at the time I set?

A few real causes: the workflow file isn't on the default branch, the repository has been inactive and GitHub auto-disabled the schedule after 60 days, or the expression uses an interval shorter than the documented five-minute minimum. GitHub also notes scheduled runs can be delayed during high-load periods, especially around the top of the hour — this tool checks the cron syntax, but can't promise the exact trigger time.

GitHub just says "you have an error in your yaml syntax" — where's the actual line?

Paste the workflow in and this GitHub Actions workflow validator reports the specific job or step name along with the line and column, instead of GitHub's generic message. Auto-run checks it again 350ms after you stop typing, so the flagged spot updates as you edit.

Why does a reusable workflow's with: block say "Unrecognized named-value: 'env'"?

Because the env context isn't available inside with: — it's one of the three documented restrictions the reference panel flags directly (env is also rejected in a step's id and uses). Move the value into a job- or workflow-level env entry, or pass it explicitly as an input to the reusable workflow instead.

Why doesn't secrets.MY_SECRET work when I paste it straight from the docs?

Most often because it's inside an if: condition, where secrets can't be read directly. Assign the secret to a job- or step-level env variable first, then check that env value in the if: condition — this is one of the three risk positions the reference panel highlights for every secrets.* reference it finds.

Why does my matrix version number 3.10 turn into 3.1?

YAML reads an unquoted 3.10 as the number 3.1, silently dropping the trailing zero — it's a YAML parsing quirk, not a GitHub Actions bug. The matrix generator flags version-shaped values and quotes them automatically when you insert the generated strategy.matrix.

Found a workflow this tool gets wrong?

Try the GitHub Actions workflow validator now

Paste a workflow into this GitHub Actions workflow validator, watch the job/step tree and reference list update, then draft a matrix or cron snippet when you need one.

Everything stays in your browser — nothing is uploaded, and no workflow is ever triggered.

All parsing happens locally in your browser.