Three characters of YAML, one dead pipeline
The deploy failed at 11:52 on a Tuesday, and the message from the CI system was its usual blend of confidence and vagueness: you have an error in your yaml syntax on line 14. Line 14 looked fine. Line 14 had looked fine for the twenty minutes you stared at it, because the problem was one tab character hiding among the spaces, and tabs are invisible in a terminal by default.
That is the canonical YAML experience, and it has held steady for fifteen years. The format runs Kubernetes manifests, GitHub Actions workflows, Docker Compose files, Ansible roles, CI configs for half the SaaS industry, and static-site settings — and it fails in ways that are real but unseeable. Indentation is structure. Invisible characters are legal text. Scalar types are guessed. Every one of those properties produces a bug that reads as a ghost.
So this page does one focused job: it parses your YAML with a spec-aware engine, and when something is wrong it tells you exactly where — line and column, not a vague neighborhood — plus a short diagnosis of the likely cause. It also runs the checks that catch the silent failures, where the file parses cleanly and the data quietly means something you did not intend.
It runs entirely in your browser, which we will justify properly near the end. Config files are among the most sensitive text people paste anywhere: hostnames, resource names, IAM bindings, and the kind of internal detail that should not take a round trip through a stranger's server to be told there is a tab on line 14.
Checking a document in sixty seconds
Paste your YAML
Copy the whole file — the failing workflow, the Helm values, the Compose file — into the editor pane. Multi-document files are welcome; each block starting with --- is treated as its own document.
Validation runs as you type
Every keystroke re-parses. A valid document shows a green state immediately; an invalid one shows the first offending position with its line and column, and the editor jumps there so you are never left counting lines by hand.
Read the diagnosis, not just the position
Alongside the location you get a plain-language guess at the cause — tab in indentation, missing space after a colon, unclosed quote, duplicate key — because the fix is the part you actually need.
Check the resolved view
For valid documents, the parsed result is shown as structured output. Reading what the parser understood is the fastest way to catch silent type surprises, such as a value that stopped being a string.
Fix and re-paste
Round-trip against your real editor, or edit inline and watch the state change. When the indicator stays green, commit — the file you validated is the file that ships.
The rules this validator enforces
YAML 1.2 is the current specification, last revised as version 1.2.2, and the grammar underneath the validator follows it. Structure comes from indentation of spaces; mappings use a colon followed by a space; sequences use a dash followed by a space. That spartan rule set is why a two-character mistake can restructure an entire document: there is no brace or tag to catch the drift.
Three structural rules generate most hard errors. First, indentation must be consistent within a level — you cannot mix widths, and a tab can never contribute to indentation, a prohibition the spec states flatly. Second, a colon inside a plain scalar needs care: key: value is a mapping, while key:value without the space is a single string in most contexts. Third, flow punctuation — the square brackets and curly braces borrowed from JSON — must be balanced, and unbalanced ones are the usual cause of errors reported far away from the actual typo.
Beyond the grammar sit the semantic checks. Duplicate keys in one mapping are reported, because overwriting a silent twin is how production gets a stale value with fresh-looking code. Anchors and aliases — the &label and *label pair that deduplicate repeated blocks — are resolved, and a missing alias target is an error rather than a shrug. Merge keys, the << shorthand for folding one mapping into another, are honored the way Ansible and Compose expect.
Documents can also be stacked: a file separated by --- markers is a stream of documents, not one broken file. This validator parses them independently and labels results per document, which matters when your values file carries per-environment blocks and only one of them is broken.
# One file, three documents:
---
app: web
replicas: 2
---
app: worker
replicas: 4
---
app: cron
replicas: 1The Norway problem, explained properly
No discussion of YAML's sharp edges is honest without this one. In YAML 1.1, the spec's implicit typing rules turned a long list of bare words into booleans: yes and no, on and off, y and n, in any capitalization. Norway's ISO country code is NO. So a config listing supported countries — with entries like NO: Norway — parsed, under 1.1 resolvers, as no: false. An entire country silently became a boolean.
The story has become the format's most famous bug class because it generalizes: the danger is never specifically Norway. It is every value that looks like something else. Version 3.10 parses as the number 3.1, dropping the trailing zero and breaking semver comparisons. The Norway entry is merely the one with a flag.
| What you wrote | What a YAML 1.1 parser stored | What YAML 1.2 stores |
|---|---|---|
| NO | false (boolean) | the string NO |
| yes | true (boolean) | the string yes |
| on | true (boolean) | the string on |
| 0777 | 511 (octal) | the string 0777 — 1.2 writes octal as 0o777 |
| 1:30 | 90 (sexagesimal) | the string 1:30 |
| 3.10 | 3.1 (float) | 3.1 (float) — still quote it |
YAML 1.2's core schema fixed this by cutting implicit booleans down to true and false and their case variants, dropping octal's bare form, and removing sexagesimal arithmetic entirely. But you cannot relax, because the ecosystem did not march in step: plenty of runtimes and libraries still resolve scalars with 1.1-era rules for compatibility. The defensive habit is simple and permanent — quote anything that must survive as a string: 'NO', '3.10', '0777'.
Worked example: a workflow file that will not run
Below is a realistic failure, the kind that arrives on a Friday. The workflow file came from a colleague who edited it in a chat window before pasting it back. Two things are wrong with it, and only one of them is visible.
Input
name: deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install
run: |
npm ci
env:
CI: trueOutput
Syntax error — line 13, column 1: found a tab character where indentation expects spaces. Note: GitHub Actions would reject this file at parse time with a generic syntax error and no position.
The tab is on the npm ci line, smuggled in by the chat app's editor. The validator names the line, the column, and the character class, which turns a twenty-minute squint into a ten-second fix. Delete the tab, indent with spaces, re-paste.
The second problem survives that fix, and it is the instructive one. The Install step's script block ends at the env key, but the indentation of env sits at the same level as run — so env belongs to the step, which is intended. Now look again at what the parsed document actually contains, which is what the resolved view is for:
Input
steps:
- uses: actions/checkout@v4
- name: Install
run: |
npm ci
env:
CI: trueOutput
Valid YAML. jobs.build.steps[1].run = 'npm ci\n', jobs.build.steps[1].env = { CI: true } — the block scalar keeps its trailing newline; GitHub Actions accepts this shape and the workflow runs.One more from the files people actually paste here — a countries block that demonstrates the Norway problem live. Under this page's YAML 1.2 parsing the string survives. Paste the same block into an older resolver and you can watch a country disappear:
supported:
NO: Norway # 1.1 parsers: no: false
SE: Sweden
DK: Denmark
version: '3.10' # quotes keep the zeroInvisible errors: tabs, spaces, and lookalike characters
The single most common failure pasted into this validator is a tab character in indentation. Editors that convert tab keypresses to spaces protect you while you type, but not when you paste, which is why the failure correlates with copying from browsers, chat apps, and slide decks. The error's reported position can sit one or two lines after the tab itself, because the parser only trips once the line's structure is unambiguous.
The tab's evil twin is the non-breaking space, Unicode U+00A0. Rich-text editors produce it to keep words together, and it renders identically to a normal space almost everywhere. Parsers do not accept it as indentation. If a line looks perfectly aligned and still fails, non-breaking spaces are the prime suspect — delete the leading whitespace and retype it.
- Tabs in indentation — forbidden by the spec, invisible on screen, extremely common in pasted text
- Non-breaking spaces (U+00A0) — look identical to spaces, rejected as indentation
- Trailing spaces after a block scalar indicator (|) — change how the block is parsed
- Windows CRLF line endings mixed into files edited on both platforms — mostly tolerated, occasionally fatal next to block scalars
- Zero-width characters from AI-chat copy-paste — a modern entry to the list, and rising
Then there are the errors that are not characters at all. Duplicate keys parse fine under lenient resolvers and then overwrite each other, so the value that ships is whichever twin came last. The validator reports duplicates explicitly for this reason: a silent bug is worse than a syntax error, because nobody is looking for it.
And one boundary worth drawing in ink: this tool validates YAML, not Kubernetes. A manifest can be flawless YAML and still reference a field your cluster version dropped. The platforms' own validators — kubectl's strict field checking, actionlint for workflows, docker compose config — are the second gate. Syntax here, schema there; you need both, in that order.
Anchors, aliases, and merge keys, briefly explained
YAML has a reuse mechanism that many people meet before anyone explains it. A node can be labeled with an anchor — &defaults, say — and referenced later with an alias, *defaults, which copies the labeled node without any literal duplication. Compose files and GitLab CI use this constantly for environment blocks that repeat with one field changed.
defaults: &defaults
image: node:22
retry: 2
lint-job:
<<: *defaults
script: npm run lint
test-job:
<<: *defaults
script: npm testThe << key is the merge operator: it folds the anchored mapping's keys into the current mapping, and keys written locally win the collision. So both jobs above inherit image and retry, and each carries its own script. The power is obvious; the failure modes are quieter. An alias whose anchor does not exist is a hard error — and a good one, because the validator names it. An anchor defined in one document of a stream is invisible to the documents after it, which is why multi-doc values files sometimes fail only on their second block. And heavy merging produces files that are flawless YAML and still surprise their authors, because the effective config was assembled from three places at once.
One more entry in the Norway family deserves its own paragraph, because half the people reading this have a file starting with the key on. GitHub Actions triggers are declared under on:, and under a YAML 1.1 resolver the bare word on resolves to boolean true — so strict linters have been known to flag the key as Truthy, and some tooling has historically shown it as ON or True after a round trip through a 1.1 parser. Nothing is broken in practice with modern parsers, but it explains a genuinely confusing class of linter warning, and it is the same mechanism that ate Norway: implicit typing, applied to a word that was doing a job.
Five errors and their five-second fixes
The parser's vocabulary is precise but austere. Translated into the language of the person holding the file, the five most common errors map to five mechanical fixes:
| What the parser says | What it means | The fix |
|---|---|---|
| A tab was found where indentation expects spaces | A literal tab character sits in the leading whitespace | Delete the leading whitespace on that line and retype it as spaces |
| Mapping values are not allowed in this context | Usually a missing space after a colon, or a colon inside an unquoted value | Write key: value with a space, or quote the value containing the colon |
| Did not find expected node content | An unclosed quote or bracket upstream of the reported position | Check the quote or bracket that opened before the reported line |
| Duplicate key in mapping | Two identical keys at the same level; the second silently wins in lenient parsers | Rename one or delete the stale twin |
| Found undefined alias | A *reference points at an anchor name that was never defined, or lives in another document | Fix the anchor spelling, or move the anchor above its first use |
A pattern worth noticing in that table: the reported position of a YAML error is where the parser lost its footing, not necessarily where the text went wrong. Unclosed quotes are the champions of this displacement — an opening quote on line 9 can push the failure report onto line 14. When a position looks innocent, look upward for the real culprit before you look sideways.
Who pastes YAML into a validator every week
Platform and DevOps engineers are the core audience: the people for whom a red pipeline is a bad morning and a values file is a production artifact. Their pattern is validate-before-commit, and their most-pasted artifacts are Kubernetes manifests, Helm values files, GitHub Actions workflows, and Docker Compose files.
Technical writers arrive with a different set: mkdocs.yml, Jekyll's _config.yml, Netlify and Vercel config files. Their YAML fails for the same reasons everyone else's does, but their tooling reports errors less helpfully, so an exact line-and-column callout saves disproportionate time.
Data scientists paste model configs and experiment files; teachers paste assignment scaffolding before handing it to students; interview candidates practice on exactly the kind of tricky document this page's examples come from. What unites them is the moment of doubt — did I break this file before I push it to a place where breaking it is expensive?
- Pre-commit checks on manifests, workflows, and values files
- Debugging CI failures whose error messages lack a usable position
- Sanitizing YAML that traveled through chat apps, docs, or wikis
- Verifying multi-document files where only one block is suspect
- Teaching and studying the format's genuinely weird scalar rules
YAML versus JSON: picking sides on purpose
The comparison is fair because the formats overlap deliberately: YAML 1.2 was designed so that all JSON is valid YAML. Comments, multi-document files, anchors for deduplication, and block structure without braces — those are YAML's gifts, and they are why humans write it. Strictness, one canonical serialization, and tooling ubiquity — those are JSON's, and they are why machines exchange it.
| Concern | YAML | JSON |
|---|---|---|
| Human writing and editing | Designed for it: comments, multi-doc, anchors | Tolerable; comments only by convention |
| Parser strictness | Famously forgiving, famously surprising | One grammar, near-zero ambiguity |
| Scalar guessing | Implicit types, version-dependent quirks | Every value carries its type on its face |
| Machine-to-machine exchange | Workable but unusual | The default of the web |
| Failure mode | Silent: wrong value, valid file | Loud: parse error, no file |
The practical split we see in real repos: humans write YAML, machines emit JSON, and the YAML-JSON Converter shuttles between the two when a config needs to feed a program that only speaks JSON. Converting a file to JSON is itself a decent smoke test — if the JSON that comes out surprises you, your YAML meant something you did not intend.
When neither fits — when you want human-writable structure with strict, obvious typing — the ecosystem has been drifting toward TOML for application settings. Its tables and arrays are fussier to write but nearly impossible to misread, and if your project is considering the switch, the TOML Formatter shows what the other side looks like. For JSON's own class of mistakes, the JSON Formatter & Validator is the sibling tool to this one.
Parsing happens in your tab
Configuration files leak infrastructure. Service names, replica counts, bucket paths, IAM roles, secret placeholders, cron schedules that reveal your deployment windows — people validate the most sensitive files their team owns, and that is precisely why the validator was built to run without a backend.
Mechanically: the text you paste stays in the page's memory, and an in-browser parser walks it there. No request carries your YAML to any server, no copy is stored, no account exists to store it under. You can pull your network cable after the page loads and validation keeps working, which is the cheapest and most convincing audit there is.
The same policy covers every sibling tool on this site, because it is a policy and not a per-page accident: files and text are processed on your device unless a step is physically impossible without a server, and none of the data tools are. A validator that phones home would be a strange design for a tool whose users include security teams.
Questions people ask before trusting a YAML checker
What does this YAML validator check?
It parses your document against the YAML 1.2 grammar, reports every syntax error with its exact line and column, handles multi-document files separated by ---, resolves anchors and aliases, and flags the classic silent failures such as unquoted version strings and country codes that parse as booleans in older parsers.
Why is my YAML failing on a line that looks perfect?
Nine times out of ten, an invisible character is the culprit: a tab where spaces belong, or a non-breaking space pasted from a chat app or rich-text editor. The error is reported where the parser stalled, which can be a line or two after the bad character. Toggle visible whitespace if your editor supports it.
What is the Norway problem in YAML?
YAML 1.1 parsers treated the bare words yes, no, on, off, y and n as booleans, case-insensitively. The two-letter country code NO therefore parsed as false, which quietly corrupted configs that listed countries. YAML 1.2 removed those implicit booleans, but many runtimes still ship 1.1-era resolvers, so quoted strings remain the safe habit.
Does the validator support multiple documents in one file?
Yes. Files that begin entries with --- split into separate documents, and each one is validated on its own. The results are labeled by document index so you can find which block in a multi-doc values file is the broken one.
Is my YAML uploaded anywhere?
No. Parsing runs in your browser with JavaScript, on the text you pasted. There is no server call carrying your configuration, which matters, because the YAML people validate here is full of hostnames, secrets placeholders, and internal infrastructure detail.
Why does 3.10 become 3.1 in my config?
Because unquoted 3.10 parses as a number, and numbers do not keep decorative trailing zeros. A version like 3.10 must be quoted ('3.10') to stay a string. The same trap swallows part numbers, chapter numbers, and anything that looks numeric but is really a label.
Can a document pass validation and still break my deployment?
Yes, and that gap is worth understanding. Syntax validation proves the file parses; it does not prove the structure matches what Kubernetes, GitHub Actions, or Docker Compose expects. Validate here for syntax, then let the target platform's schema check catch wrong or missing fields.
Tabs or spaces for YAML indentation?
Spaces — always. The YAML specification forbids tabs for indentation, full stop. Most editors convert a tab keypress into spaces automatically, but pasted text bypasses that courtesy, which is why pasted YAML fails so often at exactly one line.
Neighboring tools and primary sources
If this page earned a bookmark, its neighbors will earn theirs: the YAML-JSON Converter for round-trips into programs, the TOML Formatter for the strict-typed alternative, and the JSON Formatter & Validator for the format on the other side of the fence. All three follow the same nothing-leaves-the-browser rule.
Sources and further reading
- YAML 1.2.2 Specification — yaml.org(yaml.org)
- GitHub Actions — Workflow Syntax Reference(docs.github.com)
- Kubernetes — Configuration Good Practices(kubernetes.io)
- Docker — Compose File Reference(docs.docker.com)
We cite primary sources (standards bodies, government agencies, official documentation) wherever they exist so you can verify every number on this page yourself.