Why does my YAML fail to parse?
YAML Indentation: Why It Breaks and How It Really Works
YAML usually fails to parse because two lines that should be siblings are indented by different amounts, because a tab was used for indentation, or because a colon, dash or # was read as syntax rather than text. YAML uses indentation instead of brackets to express nesting, and its rules are stricter and simpler than they look: indentation must be spaces; any number of spaces is allowed, but every item at one level must start in exactly the same column; a child must be indented further than its parent; and the only exception is that a list's dashes may sit at the same column as the parent key. Nearly every indentation error is a violation of one of those four rules, and the parser usually reports it on the line after the real mistake.
The four rules
1. Spaces only
The YAML 1.2 specification forbids tab characters in indentation. Tabs can appear inside a line in some places, but never as the leading whitespace that decides nesting. Editors that insert tabs on Enter, and copy-pasting from a document that used tabs for alignment, are the usual sources. The parser reports it directly:
Tabs are not allowed as indentation (line 2, column 1)Set your editor to insert spaces for YAML files. An .editorconfig entry with indent_style = space for *.yml and *.yaml fixes this for everyone on a team.
2. Siblings line up exactly
The amount of indentation is not fixed. Two spaces is the common convention, four is fine, and a file can even use different amounts at different depths. What must hold is that all keys of one mapping (and all items of one list) start at the same column. Paste this into the YAML Formatter:
services:
web:
image: nginx
ports:
- "80:80"ports is meant to be a sibling of image, but it starts one column to the left. The parser cannot treat it as a child of web (it is not aligned with image) or as a sibling of web (it is not aligned with that either), so it stops:
All mapping items must start at the same column (line 4, column 1)
This line is indented differently from its siblings. Items at the same level must line up exactly.Move ports to column 5, matching image, and it validates. The formatter then rewrites the file with consistent two-space indentation.
3. Children are indented further than their parent
A key whose value is a nested mapping ends with a colon, and the nested keys must start in a column greater than the parent key's column. Here is the dangerous part: if you forget to indent, the result is often still valid YAML, just with a different meaning.
parent:
child: 1This parses without error as two top-level keys, parent with a null value and child with the value 1. No parser can tell you that this was a mistake. Validation catches syntax; only a schema, or the program reading the file, catches structure. When a setting seems to be ignored, check that it is nested where you think it is by converting the file with YAML to JSON and reading the braces.
4. List dashes may share the parent's column
A sequence under a mapping key can be written two ways, and both are valid:
# Indented sequence
containers:
- name: web
image: nginx
# "Indentless" sequence: dash at the parent's column
containers:
- name: web
image: nginxThe specification treats the - indicator as part of the indentation for whatever follows it, which is why the second form is allowed. The YAML Formatter normalises to the first form. Pick one per file; mixing them is legal but makes siblings hard to see.
The more common mistake is inside a list item. When an item is a mapping, its first key follows the dash, and every other key must line up with that first key, two columns right of the dash:
- name: web
image: nginx # aligned with "name": correct
- name: sidecar
image: busybox # one column too far: errorReading the column grid
It helps to see indentation as columns rather than as "levels":
col: 1 3 5 7 9
spec:
replicas: 3
template:
spec:
containers:
- name: web
image: nginx
ports:
- containerPort: 80Every key's column is determined by its parent's column plus some positive amount. name, image and ports share a column because they are keys of the same mapping (the list item). - containerPort is a new list nested under ports. If one line in that block drifts by a single space, the parser either rejects the file or, worse, re-parents the line.
Block scalars: indentation inside strings
Multi-line strings use | (literal, keeps newlines) or > (folded, joins lines with spaces). Their content must be indented further than the key, and the first non-empty line sets the indentation for the whole block. A later line with less indentation ends the block, so this is an error:
script: |
echo one
echo two
next: 1The parser sees echo two at column 2, which is neither inside the block nor aligned with script, and reports "All mapping items must start at the same column" on line 3.
| Header | Name | Line breaks inside | Final newline | Example result |
|---|---|---|---|---|
| |
Literal, clip | Kept | Exactly one | "line one\n indented\nline three\n" |
> |
Folded, clip | Single breaks become spaces; blank lines become breaks | Exactly one | "first half second half\nnew para\n" |
|- |
Literal, strip | Kept | None | "no newline" |
|+ |
Literal, keep | Kept | All trailing newlines kept | "kept\n\n" |
|2 |
Literal with indentation indicator | Kept | Exactly one | " four spaces\n" |
The results in the last column come from converting this test file with the YAML to JSON converter:
literal: |
line one
indented
line three
folded: >
first half
second half
new para
strip: |-
no newline
keep: |+
kept
indicator: |2
four spaces
after: xThe indentation indicator (|2) is needed when the content itself starts with spaces, such as an indented code sample; it tells the parser how many columns belong to the block's indentation, so the rest is preserved.
Multi-line plain values and flow collections
Unquoted (plain) values can continue onto following lines, as long as each continuation line is indented further than the key. The line breaks fold into single spaces, so
description: A long sentence that
continues hereloads as "A long sentence that continues here". If the continuation line drifts back to the key's column, the parser reads it as a new key and fails, usually with "Implicit keys need to be on a single line". Flow collections ([a, b] and {a: 1}) ignore indentation inside the brackets, but their continuation lines must still be indented further than the parent key. When a value needs line breaks preserved, use a block scalar instead.
Errors that look like indentation but are not
| Message | Real cause | Fix |
|---|---|---|
| Implicit keys need to be on a single line | A line like key:value with no space after the colon is a plain string, and the next line turns it into a multi-line key |
Add a space: key: value |
| Nested mappings are not allowed in compact mappings | A value containing : , such as msg: hello: world |
Quote the value: msg: "hello: world" |
| Map keys must be unique | The same key twice in one mapping, often after a merge conflict | Delete one; YAML 1.2 requires unique keys |
| Flow sequence ... must be sufficiently indented and end with a ] | An unclosed [ (or {, with "Flow map" in the message). The formatter's hint says so directly — "A [ or { is not closed, or items inside it are missing commas." — and does not suggest re-indenting |
Close the bracket, or use block style |
| Sequence item without - indicator | A line inside a list item that is less indented than the item's first key | Align it with the first key after the dash |
Also watch for #. A comment starts at # only when it is preceded by whitespace, so url: http://example.com/#top keeps the fragment, while color: #ff0000 is a key with a null value and a comment. Quote values that begin with an indicator character (#, &, *, %, @, |, >, [, {, a backtick or a tag marker), or that start with - or ? .
A debugging routine
- Validate with the YAML Formatter and go to the reported line. Then look one or two lines above it; the parser reports where it noticed the problem, which is often after the line that caused it.
- Turn on visible whitespace in your editor. Tabs and odd numbers of spaces are obvious once they are drawn.
- If the file parses but behaves wrongly, convert it to JSON and check that each key sits inside the object you expect.
- Once it is valid, let the formatter normalise indentation so the next edit starts from a consistent file.
- For the values themselves, especially codes like
NO, versions like1.10and times like12:30, see the conversion gotchas in JSON vs YAML.