YAML Indentation Rules: Spaces, Tabs, and the Errors You Never Get
Most YAML indentation advice is a style guide wearing the costume of a specification. These are the rules the parser actually enforces — including the cases where misaligned indentation produces no error at all, just a document that quietly means something else.
The rules that are actually rules
- 1. Indentation is spaces only — never tabsThis is the one hard, universal rule. A tab used for indentation is rejected outright with a message that names it, which makes it the easiest YAML error to fix.
- 2. Siblings must share a column exactlyKeys in the same mapping must begin at the same column. One space of drift between two sibling keys is an error, not a rounding difference.
- 3. A child must be indented more than its parentHow much more is up to you. Any increase opens a nested block, and the block ends at the first line indented less.
- 4. Depth is relative, not absoluteThe parser cares that a child is deeper than its parent, not that it sits at column 2 or 4. Two different blocks in one file may use different depths and both are valid.
Two spaces is a strong convention and worth following, especially in a repository other people edit. It is not what the parser checks. Knowing the difference is what lets you read an unfamiliar file that uses four and recognise it as correct rather than as the bug you are hunting.
What the parser did with each of these
| Input | Outcome | Result |
|---|---|---|
| parent: a: 1 b: 2 | Parses | { parent: { a: 1, b: 2 } } |
| parent: a: 1 b: 2 | Parses | Identical result. The count is convention, not a rule. |
| a: b: 1 c: d: 2 | Parses | Valid. Two blocks, two different depths, one file. |
| parent: → a: 1 | Error | Tabs are not allowed as indentation at line 2, column 1 |
| a: "has→ tab" | Parses | Valid. The ban is on indentation, not on the character. |
| parent: a: 1 b: 2 | Error | Nested mappings are not allowed in compact mappings at line 2, column 6 |
| items: - one - two | Parses | { items: [one, two] }. A sequence need not be indented under its key. |
| items: - one - two | Parses | Identical result. Both spellings are correct. |
| items: - one - two | Parses — wrongly | { items: ["one - two"] } — one string, no error |
| a: b: 1 | Parses — wrongly | { a: null, b: 1 } — no error |
The tab row shows the tab as → so it is visible. In your editor it is invisible, which is exactly why it is worth turning on whitespace rendering for YAML.
Spaces, tabs, and why only one is banned
Tabs are rejected because YAML has no tab width to assume — the same file would nest differently in two editors. Inside a quoted value there is no column to compute, so a tab is just a character.
So a: "has\ttab"is fine and a tab at the start of a line is not. The rule is about position, not about the character — which is why “YAML does not allow tabs” is close enough to be useful and wrong often enough to confuse.
Lists: where the dash column confuses people
A sequence under a key may sit at the same column as the key or be indented under it. Both are correct and produce the same document, which is why you see both in the wild and why neither is the bug you are looking for.
Sequences of mappings are the part worth learning properly:
items:
- name: a
id: 1
- name: b
id: 2Inside a sequence item, keys align with the first key after the dash — not with the dash itself. The dash and its space count as indentation, so `name` sits two columns right of the `-`, and every sibling key must match `name`.
Aligning the second key with the dash instead is the common slip:
items:
- name: a
id: 1That gives All mapping items must start at the same column at line 3, column 1, because id at column 3 is no longer inside the sequence item and does not line up with items either.
The cases that do not error at all
These two parse. Nothing warns you, because both are legal YAML that means something other than what was intended: a plain scalar may span lines, so a misaligned list item is read as a continuation of the previous one, and a key with nothing after the colon is a key whose value is null.
items:
- one
- two -> items: ["one - two"]One extra space turned two list items into a single string containing a hyphen. The file loads, the deployment proceeds, and the list has one entry.
a:
b: 1 -> { a: null, b: 1 }A key with nothing after it is a key with a null value — normal YAML, and indistinguishable from a value you forgot to indent underneath.
This is the reason to check YAML by reading the parsed result rather than by seeing whether it errors. An error is the good outcome. The dangerous file is the one that loads.
Check a file
Pasting into the YAML Formatter shows the parsed structure, which is the only reliable way to catch the silent cases above — you are checking what the document means, not whether it survives.
It reports the line and column where parsing stopped, and it does not reindent or repair anything: a file with a tab produces the error and no output. Parsing runs in your browser, so a manifest with real values is not uploaded anywhere.
Related
If you already have an error and want to know why it points at the wrong line, YAML “did not find expected key” compares how three parsers report the same misalignment.
Measured with
- DataToolsHQ YAML Formatter —
yaml (npm) 2.9.0
Every outcome in the table was produced by running the input through this parser. Error wording differs between YAML implementations; the rules underneath are the same everywhere.