Common YAML Mistakes and How to Fix Them

YAML is meant to be easy to read, but its rules for indentation and its habit of guessing types cause a steady stream of broken configs. These are the mistakes behind most Kubernetes, Docker Compose, CI and OpenAPI file errors, with the fix for each.

Tabs and indentation

YAML forbids tab characters for indentation. A file saved with tabs fails with messages like found character that cannot start any token or mapping values are not allowed here. Configure your editor to insert spaces, show whitespace, and use two spaces per level as the convention.

Indentation defines structure, so a single misplaced space changes meaning without a syntax error:

# env is a child of container
container:
  image: app
  env:
    - name: MODE
      value: prod

# env is now a sibling of container
container:
  image: app
env:
  - name: MODE
    value: prod

Both are valid YAML but describe different documents. List items under a key may be indented or not (- a at the same column as the key is allowed), which confuses diff tools; pick one style and let a formatter enforce it. Run the file through the YAML validator to see the exact line and column of a problem.

The Norway problem and other guessed types

A plain, unquoted YAML value is not always a string. Parsers decide its type by pattern. In YAML 1.1, which PyYAML and many other libraries still implement, these become booleans: y, n, yes, no, on, off, true and false, in several capitalisations.

countries:
  - GB
  - NO      # becomes false
  - FR

This is called the Norway problem, because the country code NO turns into the boolean false. YAML 1.2 narrowed booleans to true and false, but libraries differ in which version they follow, so the safe habit is to quote any string that could be read as something else. The same guessing affects:

  • Versions: version: 1.10 becomes the float 1.1, losing a digit. Write "1.10".
  • Special keys: on: in a GitHub Actions file can parse as true in YAML 1.1 parsers.
  • Nulls: ~, null and an empty value are all null, so name: with nothing after it is null, not an empty string.
  • Times: in YAML 1.1, 12:30:45 can parse as a base-60 integer. Quote it.
  • Dates: 2026-10-06 may become a date object rather than a string, depending on the library.

When in doubt, quote it. A formatter such as the YAML formatter will show the resulting structure, and converting with YAML to JSON shows exactly which types the parser chose.

Octal numbers and file modes

A number with a leading zero is octal in YAML 1.1, so mode: 0755 is the integer 493, which happens to be the right value for rwxr-xr-x. The surprise comes with values that look decimal but use only octal digits: zip: 02134 becomes 1116, while zip: 08540 is not valid octal, so many parsers read it as a string or a decimal number instead.

YAML 1.2 changed the prefix to 0o755, so the same text means different things in different tools. File permission values in Compose and Kubernetes manifests are a common place to hit this. Quote anything that is not meant to be a number, and double-check mode values by converting the file to JSON and reading the number the parser produced.

Colons, quotes and special characters

A plain scalar cannot contain : (a colon followed by a space) or # (a space followed by a hash), because they start a mapping value or a comment. These lines fail or silently truncate:

title: Warning: disk full        # error: mapping values are not allowed here
command: echo done # finished   # the value is "echo done"

Quote values that contain them. A value also cannot start with characters that have a meaning of their own: * and & (aliases and anchors), ! (tags), %, @, a backtick, | or > (block scalars), [, { and - followed by a space.

Choose quotes deliberately. Single quotes take everything literally, so a backslash is just a backslash and a quote inside is written ''. Double quotes process escapes such as \n and \u00e9. For multi-line text, block scalars (| and >) are cleaner than quoting; the guide to YAML multiline strings explains the indicators for keeping or stripping the final newline.

Anchors, aliases and duplicate keys

Anchors (&name) and aliases (*name) let you reuse a block, and the merge key << copies a mapping into another:

defaults: &defaults
  retries: 3
  timeout: 30
service:
  <<: *defaults
  timeout: 60

Here service ends up with retries: 3 and timeout: 60, because keys written directly win over merged ones. Two cautions apply. The merge key comes from YAML 1.1 and is not part of the 1.2 core schema, although most libraries still support it, so check yours. And aliases are resolved by the parser, so a tool that reads the raw text, such as a linter or a search, will not see the merged values.

Duplicate keys are the other quiet failure. The specification says keys must be unique, but many parsers keep only the last value and say nothing, so an accidentally repeated image: silently overrides the first. Use a validator that reports duplicates, and keep one key per mapping.

A short prevention checklist

  • Use spaces only, with the same indentation width throughout.
  • Quote strings that could be read as booleans, numbers, dates, times or null: "NO", "1.10", "0755", "on".
  • Quote any value containing : , # or leading special characters.
  • Write null explicitly as null when you mean it, and "" for an empty string.
  • Run a validator or formatter in your editor and in CI, so a mistake fails the build rather than the deployment.
  • Convert to JSON when you are unsure what a parser will produce, and read the result.

If you are choosing a format for a new project and these rules sound like too many, the comparison of TOML and YAML weighs the alternatives.

Frequently asked questions

Why does YAML read NO as false?

YAML 1.1 treats yes, no, on, off, y and n as booleans, so an unquoted NO becomes false. Quote it as “NO”, or use a parser that follows YAML 1.2.

Can I use tabs in YAML?

Not for indentation. YAML requires spaces to indent, and a tab there causes a parse error. Tabs inside a quoted string are fine.

Why did my version 1.10 become 1.1?

An unquoted 1.10 matches the pattern for a number, so the parser stores the float 1.1 and the trailing zero is lost. Write the version as a quoted string.

What is the difference between single and double quotes in YAML?

Single-quoted strings are literal, with only a doubled single quote as an escape. Double-quoted strings process backslash escapes such as \n and \u00e9.

How do I check what types a YAML parser produced?

Convert the file to JSON and read the result. Booleans, numbers, nulls and dates show up with their real types, which makes any unwanted guessing obvious.

Related