Why does my YAML fail to parse?

YAML Indentation: Why It Breaks and How It Really Works

· Open the YAML Formatter

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:

Text
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:

One key indented by three spacesOpen in 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:

Text
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.

YAML
parent:
child: 1

This 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:

YAML
# Indented sequence
containers:
  - name: web
    image: nginx

# "Indentless" sequence: dash at the parent's column
containers:
- name: web
  image: nginx

The 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:

YAML
- name: web
  image: nginx      # aligned with "name": correct
- name: sidecar
   image: busybox   # one column too far: error

Reading the column grid

It helps to see indentation as columns rather than as "levels":

Text
col: 1 3 5 7 9
     spec:
       replicas: 3
       template:
         spec:
           containers:
             - name: web
               image: nginx
               ports:
                 - containerPort: 80

Every 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:

YAML
script: |
  echo one
 echo two
next: 1

The 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:

YAML
literal: |
  line one
    indented
  line three
folded: >
  first half
  second half

  new para
strip: |-
  no newline
keep: |+
  kept

indicator: |2
    four spaces
after: x

The 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

YAML
description: A long sentence that
  continues here

loads 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

  1. 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.
  2. Turn on visible whitespace in your editor. Tabs and odd numbers of spaces are obvious once they are drawn.
  3. If the file parses but behaves wrongly, convert it to JSON and check that each key sits inside the object you expect.
  4. Once it is valid, let the formatter normalise indentation so the next edit starts from a consistent file.
  5. For the values themselves, especially codes like NO, versions like 1.10 and times like 12:30, see the conversion gotchas in JSON vs YAML.

Tools used in this guide