YAML formatter/validator · Guide
YAML Indentation Explained: Spaces, Nesting and the Rules That Trip You Up
Indentation is not formatting in YAML — it is the syntax. Where a brace would delimit a block in JSON, YAML uses the column a line starts in, which means whitespace that looks decorative is load-bearing. Most YAML errors are one of about five indentation mistakes, and they are much easier to spot once you know which five.
Indentation is the structure
Each level of nesting sits one consistent step further right than its parent, and that step must use spaces. Two spaces is the common convention; four is equally valid. What matters is that siblings in the same block line up in exactly the same column.
server:
host: localhost # 2 spaces — child of server
port: 8080 # same column — sibling of host
tls:
enabled: true # 4 spaces — child of tls
cert: /etc/cert.pem
logging:
level: debug # back to 2 — child of loggingThe indent width may differ between blocks — one mapping can use two spaces and another four — as long as it is consistent within each block. Mixing widths among siblings is the error, not using a different width elsewhere in the file.
Tabs are forbidden
YAML does not allow tabs for indentation anywhere. This is a hard rule in the specification, not a style preference, and the parser rejects the file outright.
found character '\t' that cannot start any tokenThat message is unambiguous once you have seen it, but the cause is invisible in most editors. Configure your editor to insert spaces for .yml and .yaml, and turn on whitespace rendering when a file is misbehaving.
# .editorconfig
[*.{yml,yaml}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = trueTabs are legal *inside* a value — key: "a\tb" is fine. The prohibition applies only to the leading whitespace that establishes structure.
Lists: the rule that surprises everyone
A sequence under a key may be indented at the same column as the key, or deeper. Both parse identically, which is why you see both in the wild and why people assume one of them must be wrong.
# these two are the same document
ports:
- 80
- 443
ports:
- 80
- 443The - is part of the indentation for anything nested under it. A mapping inside a list item aligns with the first key after the dash, not with the dash itself.
servers:
- name: web-1 # 'name' starts the item
port: 80 # aligns with 'name', not with '-'
tags:
- prod
- name: web-2
port: 8080Putting port under the dash instead of under name is the single most common YAML mistake. It turns one mapping with two keys into either a parse error or, worse, two separate list items — which parses cleanly and deploys the wrong configuration.
Multi-line strings ignore the usual rules
Block scalars introduced by | or > capture text verbatim, and their indentation is stripped relative to the first line rather than being structural.
literal: |
line one
indented further
line three
# keeps newlines and the relative indent of line two
folded: >
this becomes
a single line
# newlines become spaces
chomped: |-
no trailing newline
kept: |+
trailing newlines preservedThe - and + suffixes control the trailing newline: | keeps exactly one, |- strips it, |+ keeps all of them. This matters more than it sounds — a certificate or SSH key embedded in a config usually fails validation because of a missing or extra final newline, not because of the key material.
Inside a block scalar you may use tabs freely, because the content is not parsed for structure.
Where indentation silently changes meaning
Some mistakes produce a parse error, which is the good case. Others produce a valid document that means something else, and those are the ones that reach production.
# intended: one item with two keys
- name: db
port: 5432
# actually written: two separate items
- name: db
- port: 5432# intended: env is a child of container
container:
image: nginx
env:
- FOO=bar
# under-indented: env is a sibling, container loses it
container:
image: nginx
env:
- FOO=barBoth of these parse without complaint. The second is the reason a Kubernetes or Docker Compose change appears to have no effect: the key moved out of the block it was meant to configure, and nothing warns you because the file is still valid YAML.
Reading the error messages
YAML parse errors point at the line where the structure became impossible, which is often one or two lines after the mistake — much like a missing bracket in code.
mapping values are not allowed in this context
-> a 'key: value' appeared where a scalar or list item was expected;
usually the previous line is under-indented
bad indentation of a mapping entry
-> a sibling key does not line up with the others in its block
could not find expected ':'
-> often an unquoted value containing a colon, e.g. url: http://x
found character '\t'
-> a literal tab in the leading whitespaceThe colon case is worth calling out because it looks like an indentation problem and is not. A value containing : — a time, a URL, a Windows path — must be quoted, otherwise the parser reads it as a nested key.
time: 12:30 # error: parsed as a nested mapping
time: "12:30" # correct
windows: C:\path # error
windows: 'C:\path' # correctKeeping it consistent
Two spaces per level, spaces only, siblings aligned, and list items indented under the key rather than at the same column — that combination is readable and never ambiguous, even though YAML permits looser alternatives.
Validate before committing rather than after deploying. yamllint catches indentation and alignment issues that a plain parser accepts, and most CI systems can run it on changed files for essentially no cost.
yamllint config.yml
python -c "import yaml,sys; yaml.safe_load(open('config.yml'))"
yq . config.yml > /dev/nullWhen a file is already broken and the error line looks correct, check the lines above it. The parser reports where it gave up, not where the indentation first went wrong.
Frequently asked questions
Can I use tabs in YAML?
Not for indentation — the specification forbids it and parsers reject the file with found character '\t' that cannot start any token. Tabs are allowed inside quoted values and inside block scalars, where the content is not parsed for structure. Configure your editor to insert spaces for .yml and .yaml files.
How many spaces should YAML use?
Two is the common convention and four is equally valid. YAML only requires that siblings within a block align in the same column; different blocks may use different widths. Consistency within a file matters more than the specific number.
Should list items be indented under their key?
Either works. - 80 at the same column as its parent key and - 80 indented two spaces parse to the identical document. Indenting is easier to read, especially when list items are themselves mappings.
Why does my YAML parse but the setting has no effect?
Almost always a key that is under-indented and has become a sibling rather than a child. The document is still valid YAML, so nothing errors — the key is simply attached to the wrong parent. Compare the column of the key against the block it should belong to.
Why do I get "mapping values are not allowed in this context"?
A key: value line appeared where the parser expected a scalar or a list item. The cause is usually one line above — an under-indented previous line — or an unquoted value containing a colon and a space, such as url: http://x or time: 12:30. Quote values that contain colons.
How do I keep a multi-line string exactly as written?
Use a literal block scalar: key: |. It preserves newlines and the relative indentation of the content. Use |- to strip the trailing newline and |+ to keep all trailing newlines — which matters for embedded certificates and keys, where a missing final newline breaks validation.
Ready to try it?
Open the free browser-based YAML formatter/validator and apply what you just read — no sign-up, runs locally.
Open the YAML formatter/validator tool