DEV Community

Cover image for How to Fix the 10 Most Common YAML Errors
Mark Foster
Mark Foster

Posted on Originally published at devbrace.com

How to Fix the 10 Most Common YAML Errors

YAML is easy to read and surprisingly easy to break. Its structure comes from indentation, many characters have special meanings, and a value that looks like a string can quietly become a boolean or a number. The result is a deploy that fails with mapping values are not allowed here, or worse, a config that loads without any error but means something you did not intend.

This guide covers the ten mistakes behind almost every YAML problem: what the error looks like, why it happens and how to fix it.

Quickest fix: paste the file into the YAML Validator. It shows the exact line and column of the error, highlights the line, and warns about values that different parsers read differently.

Error messages below are quoted from two places: DevBrace (the free YAML Validator I built) and PyYAML, the parser behind Ansible and most Python tools. Other parsers word things differently, but the causes are the same.

1. Tabs used for indentation

server:
    port: 8080
Enter fullscreen mode Exit fullscreen mode

The YAML specification forbids tab characters for indentation, full stop. Editors that insert tabs, or copy-paste from a web page, are the usual culprits — and tabs are invisible, so the file looks fine.

  • DevBrace: Tabs are not allowed as indentation (line 2)
  • PyYAML: found character '\t' that cannot start any token

Fix: replace tabs with spaces. Set your editor to "insert spaces" for .yaml and .yml files. The YAML Formatter rewrites the whole file with consistent space indentation once it parses.

2. Inconsistent indentation

server:
  host: example.com
   port: 8080
Enter fullscreen mode Exit fullscreen mode

host and port are meant to be siblings, but port has three spaces instead of two. To a YAML parser, a different indent means a different level of nesting, so the line no longer fits anywhere.

  • DevBrace: Nested mappings are not allowed in compact mappings (line 2)

Fix: keys at the same level must start in exactly the same column. Two spaces per level is the convention for Kubernetes, Docker Compose, GitHub Actions and Ansible. When an error points to a line that looks correct, compare its indentation with the line above it.

3. No space after the colon

name:api
port: 8080
Enter fullscreen mode Exit fullscreen mode

A key and its value are separated by a colon followed by a space. Without the space, name:api is not a key at all. The parser reads it together with the next line as one long key that spans two lines, which is not allowed.

  • DevBrace: Implicit keys need to be on a single line (line 1)

Fix: write name: api. This is also why URLs work unquoted: in url: https://example.com:8443/api, none of the colons inside the value are followed by a space.

4. A colon followed by a space inside a value

message: Error: disk full
Enter fullscreen mode Exit fullscreen mode

The first : separates the key from the value. The second : makes the parser think the value is itself a key, starting a nested mapping on a single line.

  • DevBrace: Nested mappings are not allowed in compact mappings (line 1, column 10)
  • PyYAML: mapping values are not allowed here

Fix: quote the value: message: "Error: disk full". Any value containing : or # (a space followed by a hash, which starts a comment) should be quoted.

5. List items and keys mixed at the same level

items:
  - apple
  banana: 2
Enter fullscreen mode Exit fullscreen mode

A node is either a list (- item) or a mapping (key: value), never both. Here items starts as a list, then a key appears at the same indentation.

  • DevBrace: All mapping items must start at the same column (line 3)

Fix: decide what the data is. Either make every entry a list item:

items:
  - apple
  - banana: 2
Enter fullscreen mode Exit fullscreen mode

or make items a mapping with named keys. Converting the file with YAML to JSON is the fastest way to see which shape you actually wrote.

6. Unclosed quotes and brackets

title: "Hello world
next: 1
Enter fullscreen mode Exit fullscreen mode
ports: [80, 443
host: example.com
Enter fullscreen mode Exit fullscreen mode

A double-quoted string runs until the next unescaped ", even across lines, and a flow list runs until its ]. One missing character swallows the rest of the file, so the reported line is often far below the real mistake.

  • DevBrace: Missing closing "quote and Flow sequence in block collection must be sufficiently indented and end with a ]

Fix: close the quote or bracket. If the error line makes no sense, search upwards for the last ", ', [ or { that was opened on the same logical value.

7. Duplicate keys

port: 80
host: example.com
port: 8080
Enter fullscreen mode Exit fullscreen mode

The specification says keys in a mapping must be unique, but parsers disagree on what to do about it. PyYAML silently keeps the last value, while JavaScript's js-yaml and Go's yaml.v3 report an error. That means the same file can load in a Python script and fail in a Node or Go service — or load with a value you did not expect.

  • DevBrace: Map keys must be unique (line 3)

Fix: remove or rename one of the keys. Duplicates usually appear after merging two versions of a file, so check the diff that introduced them.

8. Undefined aliases — and values starting with * or &

prod:
  <<: *defaults
  replicas: 3
Enter fullscreen mode Exit fullscreen mode

In YAML, &name defines an anchor and *name refers back to it. An alias must come after its anchor, and the anchor must exist in the same document.

  • DevBrace: Unknown alias *defaults: no anchor &defaults is defined before it (line 2)
  • PyYAML: found undefined alias 'defaults'

The same rule catches values that merely look like aliases. pattern: *.log is read as a reference to an anchor named .log, not as a glob.

Fix: define &defaults above the alias, or quote text that starts with * or &: pattern: "*.log".

9. Values starting with a reserved character

email: @jane
template: { name }
Enter fullscreen mode Exit fullscreen mode

Several characters cannot start a plain (unquoted) value: @ and ` are reserved, { and [ start a flow mapping or list, ! starts a tag, | and > start a block scalar, and % starts a directive.

  • DevBrace: Plain value cannot start with reserved character @ (line 1)

The second line is subtler. { name } is valid YAML, but it is a mapping, not the text { name }. The same thing happens with the double-brace template expressions used by Ansible and Helm: an unquoted value that starts with a brace becomes a nested mapping, and PyYAML fails with found unhashable key.

Fix: quote the value: email: "@jane", template: "{ name }". Quote every template expression that starts a value.

10. Values that silently change type

This one produces no error at all, which is what makes it dangerous.

country: NO
enabled: yes
mode: 0644
version: 1.10
zip: 02134
name:
Enter fullscreen mode Exit fullscreen mode
Value YAML 1.2 parsers YAML 1.1 parsers (PyYAML, Ruby)
NO the string "NO" the boolean false
yes, on, off strings booleans
0644 the number 644 the octal number 420
1.10 the number 1.1 the number 1.1
02134 the number 2134 the octal number 1116
name: (nothing) null null

The first row is the famous Norway problem: a list of country codes where Norway turns into false. The version: 1.10 row bites everyone eventually — 1.10 and 1.1 are the same number, so the version you wrote is not the version your program reads.

The YAML Validator flags NO, yes, off and 0644 with a warning and the line number, because the file is valid but means different things to different tools.

Fix: quote anything that must stay text: country: "NO", version: "1.10", zip: "02134". If you generate YAML from JSON, the JSON to YAML converter adds these quotes for you.

A debugging checklist

  1. Validate first. Paste the file into the YAML Validator and go to the reported line.
  2. Look above the reported line. Indentation, unclosed quotes and missing colons are usually caused one or two lines earlier.
  3. Check for tabs. Turn on "show whitespace" in your editor.
  4. Quote suspicious values. Anything with :, #, a leading @, *, &, {, [, or a value like yes, no, on, off, version numbers and leading zeros.
  5. See what the parser sees. Convert to JSON with YAML to JSON: the explicit brackets and quotes make wrong nesting and wrong types obvious.
  6. Normalise the file. Once it is valid, run it through the YAML Formatter so indentation stays consistent for the next person who edits it.

Catching YAML errors before they ship

A validator in the browser is perfect while editing. In a repository, add a check to CI so a broken file never reaches production:

# Syntax check with Python (PyYAML)
python -c 'import sys, yaml; [list(yaml.safe_load_all(open(f))) for f in sys.argv[1:]]' config/*.yaml

# Style and syntax with yamllint
yamllint -d '{extends: default, rules: {truthy: {check-keys: false}}}' .
Enter fullscreen mode Exit fullscreen mode

yamllint's truthy rule catches the Norway problem, its key-duplicates rule catches duplicate keys, and indentation catches the rest.

Most YAML errors take seconds to fix once you know which of these ten you are looking at.


Originally published on DevBrace, where you can also try the YAML Validator used in the examples.

Top comments (0)