YAML Validator
Check YAML syntax in your browser, then see how PyYAML, Kubernetes and Spring Boot's real parsers would read every value.
Paste YAML to check it
Fixed — every reader now agrees
Worked example: a valid Docker Compose file
services:
api:
image: registry.example.com/cave-api:2026.09.12
ports:
- "8080:8080"
environment:
REGION: no
LOG_LEVEL: debug
restart: unless-stopped
cache:
image: redis:7
restart: unless-stopped
⚠ Valid YAML, read differently by 4 reader(s) — the REGION: no line reads as the boolean false under PyYAML, SnakeYAML and go-yaml v2. 1 document, 11 keys.
No network activity while you use this tool Show the numbers
- Requests to any other server
- 0
- Requests since you started typing
- —
- Same-origin requests
- 0
Counted live by your browser's own Performance Timeline — the same data the DevTools Network panel reads. It cannot see what a browser extension does, and it is not meant to replace checking for yourself: here is how, in thirty seconds .
What "valid" means: three questions, not one
A validator that only says "valid" or "invalid" hides the thing worth knowing. This tool
keeps three questions separate and never blurs them together. Level 1 (syntax)
asks whether the text can be parsed as YAML 1.2.2 at all — checked against the language
spec, and the only question with a strict ✗/✓ answer. Level 2 (loading)
asks whether a specific library will reject or silently change the file when it loads
it — a duplicate key is valid syntax, but PyYAML keeps the last value without a word while
js-yaml throws outright. Level 3 (reading) asks what a real parser resolves
a given value to — a bare no is text under YAML 1.2 core and the
boolean false under PyYAML, SnakeYAML and go-yaml v2. A file can be perfectly
valid at Level 1 and still surprise you at Level 2 or 3, and the headline verdict says
exactly which is true, rather than collapsing all three into one word.
The most common YAML errors
| What's wrong | Example | What PyYAML says for the same mistake |
|---|---|---|
| This line is indented differently from the lines next to it | a:
b: 1
c: 2 |
mapping values are not allowed here |
| A ":" inside this value looks like the start of another key | k: a: b |
mapping values are not allowed here |
| There's a tab character in this line's indentation | a:
b: 1 |
found character '\t' that cannot start any token |
| This double-quoted value never closes | k: "abc |
while scanning a quoted scalar, found unexpected end of stream |
| This single-quoted value never closes | k: 'abc |
while scanning a quoted scalar, found unexpected end of stream |
| This { or [ never closes | k: [1, 2 |
while parsing a flow node, expected the node content, but found end of stream |
| Expected a comma between items here | k: [1 2] |
missed comma between flow collection entries |
| A plain value can't start with this character | k: @x |
found character '@' that cannot start any token |
| This line wasn't read as part of the document | a: 1
b: 2 |
error 1:4 (unexpected content after a complete value) |
| This alias refers to an anchor that doesn't exist | a: *x |
BAD_ALIAS |
| An alias can't also carry a tag or another anchor | a: &x 1
b: !!str *x |
ALIAS_PROPS |
| This isn't a valid escape sequence | k: "\q" |
found unknown escape character |
| A directive needs a --- after it | %YAML 1.2
a: 1 |
MISSING_CHAR (directives end mark expected) |
| A list item and a mapping key can't sit at the same indent | - a
b: 1 |
end of the stream or a document separator is expected |
| A line inside this block scalar is indented less than the block itself | k: |
x
y |
BAD_INDENT |
| This implicit key is too long | — |
KEY_OVER_1024_CHARS |
Each error here shows the exact line and column, an excerpt with a caret pointing at the fault, a plain-English explanation of why YAML refuses to guess, and — in a "What other parsers say" disclosure — the real message PyYAML, go-yaml and js-yaml print for the same mistake, so a search for that exact error text lands on an answer.
Valid, but read differently
| Reader | Spec | Used by |
|---|---|---|
| YAML 1.2 core | 1.2 | js-yaml 4, yaml 2 (eemeli/yaml), ruamel.yaml, YamlDotNet, libfyaml, yq (Go, mikefarah) |
| PyYAML | 1.1 | PyYAML, Ansible, Home Assistant, pre-commit, mkdocs |
| SnakeYAML | 1.1 | SnakeYAML, Spring Boot, Jenkins (Groovy pipeline YAML) |
| go-yaml v2 (Kubernetes / Helm) | 1.1 | Kubernetes (sigs.k8s.io/yaml, historically), Helm, kubectl, kustomize |
| go-yaml v3 | 1.2-ish | Compose v2 (compose-spec reference impl), many Go CLIs |
| Psych (Ruby) | 1.1 | Ruby (Psych is the stdlib YAML library), Rails, GitLab CI |
A bare no, an unquoted on: key, a leading-zero number like
0777, or a two-digit string like 08 all mean different things to
different real parsers — some read 1e3 as the float 1000, PyYAML reads it as
the literal text "1e3" because its float grammar requires a signed exponent. The Readers
tab shows exactly which of your file's values are ambiguous this way, under every profile
at once or narrowed to the one your deployment target actually uses.
Duplicate keys: the silent case is the dangerous one
The YAML spec is unambiguous: keys in a mapping must be unique. Real parsers disagree on
what to do about a file that breaks that rule. js-yaml and the yaml
npm package both throw an error immediately. PyYAML's safe_load — the parser
behind Ansible, pre-commit and Home Assistant — keeps the last value and raises no
warning at all. That means a config block accidentally pasted twice doesn't fail loudly; it
silently overwrites itself, and the file "works" right up until the missing half is the half
that mattered. This tool always calls out a duplicate key, whichever reader you're checking
against, because the spec says it is wrong even where a library lets it through.
Lint rules, stated plainly
Lint is a different, optional layer — a trailing space never turns a ✓ into a ⚠, and lint is off by default so a first-time visitor pasting a working file isn't greeted by a wall of style warnings. When turned on, this tool uses yamllint's own rule names and default settings, for 14 of its 23 rules — the rest need a positioned token stream this engine doesn't build yet, and the page states that honestly rather than rounding up.
house/version-numberhouse/colon-without-spacehouse/dash-without-spacehouse/hash-without-spacehouse/leading-zerohouse/norway
The checks above are this tool's own opinion, never given a yamllint name, so they can never be mistaken for one of yamllint's own rules.
Checking YAML from the terminal
-
yamllint file.yaml— the reference implementation for style; PyYAML underneath, so it inherits PyYAML's YAML 1.1 value resolution as well as its own lint rules. -
python -c "import sys, yaml; yaml.safe_load(open(sys.argv[1]))"— the exact parser Ansible, Home Assistant and pre-commit use; a duplicate key here loads silently. -
yq '.' file.yaml— mikefarah'syqis a YAML 1.2-family reader, so a barenostays text, unlike Ansible's own PyYAML. -
kubectl apply --dry-run=client -f file.yaml— validates against the Kubernetes API schema (this tool does not), using go-yaml under the hood for the YAML layer itself.
Common use cases
- Checking a Docker Compose or Kubernetes manifest is syntactically valid before a deploy
- Finding the exact line a hand-edited config file broke on, with a caret pointing at it
- Confirming a bare no, yes, on or off reads the way your specific deployment target reads it
- Catching a silently duplicated key before it silently overwrites half a config block
- Getting yamllint-style findings without installing Python or yamllint locally
Frequently asked questions
- Is my YAML uploaded anywhere?
- No. Parsing, the per-reader resolution and the lint pass all run in this tab's own JavaScript. Nothing is sent to a server, logged, or stored — open the Network tab before you paste to confirm it for yourself.
- Which YAML version does it check?
- Syntax is checked against YAML 1.2.2, the current spec. Values are checked against six real readers spanning both YAML 1.1 (PyYAML, SnakeYAML, go-yaml v2) and YAML 1.2 (js-yaml/yaml, go-yaml v3, Psych's own mix) — because the honest answer to "is this valid" is different from the honest answer to "how will my deployment target read it".
- Why does it say "valid" and still warn me about no?
- Because those are two different questions. Level 1 (syntax) asks whether the text parses as YAML at all — a bare no parses fine. Level 3 (reading) asks what a real parser resolves that text to, and PyYAML, SnakeYAML and go-yaml v2 all resolve a bare no to the boolean false, not the string "no". Both facts are true at once, so the verdict shows both rather than picking one and hiding the other.
- Is a duplicate key an error?
- The spec says yes — keys in a mapping must be unique. But not every parser enforces it: js-yaml and the yaml package both throw, while PyYAML's safe_load silently keeps the last value and says nothing at all. That silent case is the dangerous one, so this tool always calls it out even though the file "loads" in some readers.
- Does it validate Kubernetes, Docker Compose or GitHub Actions files?
- It checks syntax and how the specific parser that tool actually uses would read your values — and shows a badge naming which file type it detected and why. It does not check the tool's own schema (whether a Kubernetes field name is spelled right, or a Compose key is in the right place). That is a much larger, separate feature this tool does not claim yet.
- Why does it only show the first syntax error?
- Once one structural mistake breaks the parse, everything after it is usually a knock-on effect of that first mistake, not an independent error — real parsers routinely report three or four errors for one root cause. Showing the first one precisely, with its exact line and column, is more useful than a list where most entries are noise. Fix it and check again; later errors can be caused by this one.
- Is this the same as yamllint?
- It uses yamllint's own rule names and default settings for 14 of its 23 rules — stated plainly rather than implied. The remaining rules need a positioned token stream this engine doesn't build yet. Lint is off by default; turn it on with the Lint segment above the editor.