YAML pitfalls: the Norway problem, indentation, and anchors
YAML looks like the friendly config format until it silently converts 'NO' to false. Here are the traps in YAML 1.1, why 1.2 fixes some of them, and when to switch to JSON.
The Norway problem
The most famous YAML footgun:
countries:
- GB
- IE
- NO
- FR
Parsed with a YAML 1.1 parser, NO becomes the boolean false. Norway silently vanishes.
YAML 1.1 has a long list of "boolean-ish" strings that get auto-converted unless quoted: yes, no, y, n, Y, N, true, false, on, off, plus every case variation. Country codes, chemical symbols, initials, and airport codes get eaten regularly.
YAML 1.2 (2009) fixed this โ only true and false are booleans. But most parsers still default to 1.1 for compatibility, including PyYAML's default load. Check your parser's version.
The safe habit: always quote strings that could be misinterpreted. "NO" is a string; NO is a coin flip.
Indentation is the syntax
Unlike JSON's braces and brackets, YAML uses indentation to define structure. Get it wrong and you get either a parse error or, worse, a silently different structure. Two rules save most people:
- Never use tabs. The YAML spec forbids them at the start of a line. Some parsers accept them, most don't. Configure your editor to insert spaces.
- Pick one indent width and stick to it. 2 or 4 spaces, applied consistently. Mixed indent breaks parsers.
The failure mode that costs the most time: mis-indenting a child key so it becomes a sibling.
service:
name: web
port: 8080 # port is now a child of name, not service. syntax error.
Anchors and aliases
YAML supports references โ define once, use many times:
defaults: &defaults
timeout: 30
retries: 3
production:
<<: *defaults
host: prod.example.com
staging:
<<: *defaults
host: staging.example.com
Great in principle, three problems in practice:
1. JSON has no equivalent. Converting YAML โ JSON expands every anchor, exploding file size. If you're going through JSON at any point in your pipeline, anchors buy you nothing at rest.
2. Merge keys (<<) are a YAML 1.1 extension. Not in 1.2 core. Most parsers still support them, but not all.
3. Billion laughs attack. Recursive anchors expand exponentially. A malicious 200-byte YAML can produce gigabytes of parsed data. safeLoad mitigates in some parsers; unbounded expansion is a real DoS vector for services that accept YAML from users.
Multi-line strings: three formats, subtle differences
folded: >
This becomes a single line
with spaces between the words.
literal: |
This preserves
every line break.
double-quoted: "This handles \n escape sequences and \"quotes\"."
Add - to strip trailing newlines (>-, |-), + to keep them all (>+, |+). Bare > and | "clip" โ keep one trailing newline. This is the source of unexpected extra newlines in generated YAML.
Numbers that aren't numbers
YAML 1.1 parses 012 as octal (10 in decimal). YAML 1.2 requires 0o12 for octal โ bare 012 is a string. If you have Docker port mappings like 010, quote them.
Also: 1.2e3 is a float, 1.2.3 is a string. Version numbers must be quoted.
When to switch to JSON
YAML's advantages over JSON are:
- Comments
- Multi-line strings
- Anchors / merge keys
- Less punctuation
If you're not using any of those, JSON is safer โ no boolean coercion, no indentation traps, no version differences.
The common middle ground: write YAML, ship JSON. Author config in YAML for readability, convert once at build time, ship the JSON. All the ergonomics; none of the runtime traps.
YAML โ JSON does the conversion locally in your browser, both directions, using js-yaml in YAML 1.2 mode. Nothing is uploaded โ safe for config files that contain secrets.
The comparison workflow
When YAML behaves unexpectedly, the fastest debugging trick is: convert to JSON and eyeball it. The structure that actually parsed is often not the structure you thought you wrote. If two YAML files should be equivalent but aren't producing the same behavior, convert both to JSON and diff them structurally.
JSON Diff does that structural comparison โ key order doesn't matter, so you see real semantic differences rather than incidental formatting noise.
Related tools:
- JSON Formatter โ to pretty-print the converted output.
- JSON Diff โ to compare two configs that should behave the same.