YAML for Developers: Common Indentation Errors and How to Debug Your Config
YAML is everywhere — Kubernetes manifests, GitHub Actions, Docker Compose, Ansible playbooks, Next.js config. It looks simple until a misplaced space silently breaks your deployment at 3 AM. This guide covers the errors developers hit most often and how to fix them.
Why YAML Indentation Is So Painful
YAML uses indentation to represent structure — like Python, but stricter. There are no closing brackets to catch misalignment, no linting built into most text editors by default, and error messages from YAML parsers range from cryptic to misleading.
The fundamental rules are simple, but the edge cases are many:
- Only spaces are allowed — tabs cause an immediate parse error
- The number of spaces per indentation level can vary, but must be consistent within a block
- All keys at the same level must be indented by the same amount
- Child elements must be indented more than their parent
The 5 Most Common YAML Errors
1. Tabs Mixed With Spaces
The most common source of YAML parse errors. Tabs are visually indistinguishable from spaces in most editors but cause an immediate found character '\t' that cannot start any token error.
# WRONG — tab before port:
server:
port: 8080 # ← This tab will break everything
# CORRECT — spaces only
server:
port: 8080Fix: Set your editor to insert spaces on Tab for YAML files. In VS Code: Format On Save + the YAML extension. In .editorconfig:
[*.yml]
indent_style = space
indent_size = 22. Inconsistent Indentation Within a Block
All keys in a mapping must start at the same column. Mixing 2-space and 4-space indentation within the same block causes a bad indentation of a mapping entry error.
# WRONG
database:
host: localhost
port: 5432 # ← 4 spaces instead of 2
# CORRECT
database:
host: localhost
port: 54323. Missing Space After the Colon
In YAML, a colon must be followed by a space (or newline) to be treated as a key-value separator. Without the space, the entire key:value is treated as a string.
# WRONG — treated as a single string "host:localhost"
database:
host:localhost
# CORRECT
database:
host: localhost4. Unquoted Special Values
YAML auto-infers types from unquoted scalars. This causes silent data corruption where the YAML is technically valid but the parsed value is wrong.
# YAML 1.1 type coercion surprises:
enabled: yes # → boolean true (not string "yes")
country: NO # → boolean false (Norway problem!)
version: 1.10 # → float 1.1 (trailing zero lost)
port: 08080 # → integer 4160 (octal in some parsers!)
date: 2024-01-15 # → Date object (not string)
# Safe versions (quoted):
enabled: "yes"
country: "NO"
version: "1.10"
port: "08080"
date: "2024-01-15"5. Wrong Sequence Indentation
List items (sequences) use a dash (-) and must be indented consistently. A common mistake is forgetting that the dash is part of the indentation.
# WRONG — services not indented under app
app:
name: my-app
services:
- api
- web
# CORRECT
app:
name: my-app
services:
- api
- webReading YAML Error Messages
When a YAML parser fails, the error message contains three key pieces of information:
- reason — the human-readable error type (e.g.,
bad indentation of a mapping entry) - line — the line number where the error was detected (1-indexed)
- column — the column where the parser got confused
Important: the reported line is where the parser noticed the problem, not necessarily where the mistake is. An indentation error on line 5 might be caused by a missing space on line 3. Always look at the lines before the reported error too.
Multi-line Strings: | vs >
Two block scalar indicators handle multi-line strings:
# Literal block (|) — preserves newlines
script: |
#!/bin/bash
echo "Hello"
echo "World"
# Result: "#!/bin/bash
echo "Hello"
echo "World"
"
# Folded block (>) — wraps to single line
description: >
This is a long description
that spans multiple lines
for readability.
# Result: "This is a long description that spans multiple lines for readability.
"Use | for shell scripts, code, and content where newlines matter. Use > for long prose strings where you want line wrapping in the YAML source but a single paragraph in the output.
Anchors and Aliases: DRY YAML
YAML has a built-in mechanism for reusing values: anchors (&name) and aliases (*name). This is especially useful in CI/CD configuration files.
# Define a reusable block
defaults: &defaults
environment: production
replicas: 3
timeout: 30
# Reuse it with merge key <<
api-service:
<<: *defaults
port: 8080
worker-service:
<<: *defaults
port: 8081Note: anchors and aliases are resolved during parsing. When you convert YAML to JSON, aliases are expanded — the deduplication is lost in the JSON output.
Quick Debugging Checklist
- Run your YAML through a validator (like the one below) to get the exact line/column
- Check for tabs — search-and-replace all tabs with spaces
- Verify indentation consistency — all keys at the same level should align
- Quote any value that looks like a boolean, number, date, or null (
yes/no/true/false/null/~) - If error points to line N, inspect lines N-3 to N as well
- Use a YAML-aware editor with syntax highlighting to make structure visible
Use the YAML Validator to paste your config and get instant error feedback with line/column context. For converting between YAML and JSON, use YAML to JSON.
Frequently Asked Questions
Can I use tabs for YAML indentation?▾
No. The YAML specification explicitly forbids tab characters for indentation. Only spaces are allowed. This is a hard rule with no exceptions — a tab character will cause a parse error. Configure your editor to insert spaces when you press Tab (most editors have a 'Tabs → Spaces' setting per language or project). The .editorconfig file is a portable way to enforce this across editors.
Why does YAML parse 'no' and 'yes' as booleans?▾
YAML 1.1 (still used by many parsers) defines yes, no, on, off, true, false as booleans — case-insensitive. The infamous 'Norway problem': the ISO country code 'NO' becomes boolean false in a YAML mapping. Fix: always quote these values when you intend them as strings: 'no', 'yes', 'on', 'off'. YAML 1.2 dropped the yes/no/on/off booleans, but library support varies — js-yaml's default is YAML 1.1.
How do I write a multi-line string in YAML?▾
Two block scalar styles: literal block (|) preserves newlines exactly — each line in the YAML becomes a line in the string. Folded block (>) converts single newlines to spaces, treating the block as a wrapped paragraph (double newlines become single newlines). Both styles strip leading indentation relative to the first content line. Example: description: | → 'This is line 1\nThis is line 2'. Use | for shell scripts or code, > for long prose descriptions.