YAML Indentation Rules and Why Tabs Break Everything
How YAML uses spaces to build structure, why a tab is rejected, and how one extra space turns a sibling into a child. Real parser output for each case.

YAML has no braces for blocks and no end tags. The only thing that says which line belongs to which is how far it is indented. That makes the format pleasant to read and unforgiving to edit, because a change you cannot see, one space, changes the meaning. This post states the rules, shows what the parser does when they are broken and gives the habits that keep files safe. Every output below is from a real parser.
The rules, in order
Indentation uses spaces. Tab characters are not allowed in the indentation of a block, anywhere in the file.
Lines at the same indentation belong to the same collection. Two keys at the same depth are siblings in one mapping. Two
-items at the same depth are items in one list.A deeper line is a child of the line above it. The line above is the nearest preceding line with less indentation.
The width of one level is not fixed. Two, three or four spaces all work, provided that within one collection every entry uses the same amount.
A line indented less than its previous line closes the nested collections back to the level whose indentation it matches. If no open level matches, it is an error.
Everything else about indentation is a consequence of those five. Note that rule 4 allows a file to use two spaces for one mapping and four for another, as long as each mapping is consistent. Do not do that on purpose, since people read inconsistency as a mistake.
Tabs
A tab in the leading whitespace is the classic error. The document below uses a tab before b:
a:
b: 1while scanning for the next token | found character '\t' that cannot start any token | in "<unicode string>", line 2, column 1The message does not say "tab". It says a tab character cannot start a token, and gives the line and column. The tab is invisible in most editors, and looks like four or eight spaces, so the line appears fine. The fix is to replace tabs with spaces and to configure the editor to insert spaces when you press Tab (finding tab characters hiding in a YAML file shows how to locate them across a repository).
The rule to remember is simple: no tabs in YAML files, at all.
One space too many
Inconsistent spacing within a mapping is the next most common failure:
a:
b: 1
c: 2mapping values are not allowed here | in "<unicode string>", line 3, column 5c is indented three spaces where b has two. The parser reads c: 2 as a continuation of the value of b, since a deeper line after a scalar extends it, and then meets a colon where none is allowed. The message points at the colon on line 3. It is a confusing message for a missing alignment, which is why the same text appears in many questions (YAML error: mapping values are not allowed here goes through its causes).
The inverse mistake is quieter. A line indented too little does not always raise an error, it can change the meaning:
a:
b: 1
c: 2a:
b: 1
c: 2{'a': {'b': 1, 'c': 2}} {'a': {'b': 1}, 'c': 2}In the first document c is a sibling of b, both inside a. In the second c moved two spaces left and became a top-level key. Both are valid YAML, and the application sees different configuration: perhaps c is a timeout nested under a service in one and an ignored top-level key in the other. No parser can warn you, because both documents are well-formed. A schema can (validating config YAML with JSON Schema), and so can a diff that you read.
Sequences and their indentation
For lists inside a mapping, YAML allows the dash to sit at the same level as the parent key or deeper:
a:
- x
- y
b: 1a:
- x
- y
b: 1{'a': ['x', 'y'], 'b': 1} {'a': ['x', 'y'], 'b': 1}Both load to the same structure. Teams choose one style and enforce it. The indented form (dashes two spaces in) is easier for people to read because the list visibly belongs to a; the unindented form is used by a lot of tooling output and saves a level of indentation. yamllint has a rule for it (indent-sequences) that can be set to consistent, which accepts either style within a file and rejects mixing.
Inside a list of mappings, the keys of one item line up under the first key, not under the dash:
- name: a
v: 1
- name: b
v: 2The v is indented two spaces so it aligns with name, which sits two columns after the dash. Align it with the dash instead and you have a different structure or an error.
Habits that prevent most problems
Show whitespace. Turn on visible whitespace in your editor for YAML files. A tab shows as an arrow and a trailing space as a dot. The first time you see the file that way, you find the mistakes you have been looking past.
Configure the editor. Insert spaces on Tab, two spaces per level, trim trailing whitespace on save. An .editorconfig file in the repository sets this for everyone who uses a supporting editor.
Lint on every change. yamllint flags inconsistent indentation, trailing spaces and tabs (linting YAML with yamllint). Run it in CI and in a pre-commit hook, so an indentation slip is caught before review.
Read the diff, not just the file. A change of indentation in a diff is a change of structure. Whitespace-insensitive diff options hide exactly the changes that matter in YAML, so do not use them for review.
Prefer shallow structures. A file nested seven levels deep invites errors. Flatten when you can, and split into files when you cannot (splitting a large YAML config into several files).
Use flow style sparingly. {a: 1, b: 2} and [x, y] ignore indentation. They are fine for short values and hard to maintain when they grow.
Related reading
The cluster hub is YAML syntax you need to stop guessing at. Next in line are YAML strings, quoting and when you need quotes and YAML lists and mappings without confusion. The indentation rules are specified in chapter 6 of the YAML 1.2.2 specification, and the checks in the yamllint documentation. Reformat a file in the YAML formatter.
When indentation alone is not enough to read a file
The file is very long. Past a screenful, you lose track of the level you are in. Use an editor that shows indent guides, or split the file.
Flow style is mixed in. Braces and brackets inside block style make the structure two syntaxes at once. Pick one for each section.
Multiline strings are present. Inside a literal block, indentation is content, not structure. A block scalar's own indentation rules are in YAML multiline strings with pipe and greater than.
You are generating YAML. If a program writes the file, let a YAML library handle indentation, and never build documents by string concatenation.


