YAML failures come in two kinds. The loud kind stops your docker compose up or GitHub Actions run with a parser error and a line number. The quiet kind parses fine, deploys fine, and then your Norwegian users are in country false.
We re-verified everything below against a real parser (PyYAML 6, the engine behind an enormous amount of CI tooling) while writing this — every error message and every mangled value is actual output, not paraphrase.
To check a file without installing anything, Yamlite (an app I make) validates YAML on-device on iPhone and iPad and shows errors with line and column.
The loud failures: what the error actually means
"mapping values are not allowed here"
ScannerError: mapping values are not allowed here
in "a.yaml", line 1, column 13
The most common YAML error. The value itself contains a colon followed by a space, so the parser reads a second key: title: Error: something broke fails at column 13, the second colon. A trailing colon does it too: pw: abc: fails at column 8, which is why passwords and Windows paths trip it. A colon with no space after it is fine (url: http://x.com parses). Fix: quote the value, title: "Error: something broke", pw: "abc:". In go-yaml the same mistake reads yaml: line 2: mapping values are not allowed in this context.
"expected <block end>, but found '<block mapping start>'"
ParserError: while parsing a block mapping
in "c.yaml", line 1, column 1
expected <block end>, but found '<block mapping start>'
in "c.yaml", line 3, column 2
A key is indented differently from its siblings: b: at two spaces, c: at one. The second location (line 3, column 2) is the key that drifted; the first is only where the enclosing mapping began. go-yaml calls this did not find expected key.
"found character '\t' that cannot start any token"
ScannerError: while scanning for the next token
found character '\t' that cannot start any token
in "<unicode string>", line 2, column 1
You indented with a Tab. YAML forbids tabs in indentation, full stop. This is the classic paste-from-terminal failure: your editor shows what looks like clean nesting, but line 2 column 1 holds \t. Fix: convert tabs to spaces (2 is the convention), and turn on "show invisibles" for YAML files.
"while scanning a quoted scalar … found unexpected end of stream"
ScannerError: while scanning a quoted scalar
in "<unicode string>", line 1, column 11
found unexpected end of stream
An opened " was never closed. The parser reads to the end of the file looking for the closing quote, so the reported line is where the quote opened — the actual missing character is usually much further down. Fix from the reported column, not from the end of the file.
"while parsing a flow sequence … expected ',' or ']'"
ParserError: while parsing a flow sequence
in "<unicode string>", line 1, column 8
expected ',' or ']', but got ':'
A flow collection ([80, 443 or {key: value) was never closed before the next mapping started. Common in ports: lists and inline env maps.
"while parsing a block collection … expected <block end>"
ParserError: while parsing a block collection
in "<unicode string>", line 2, column 3
expected <block end>, but found '<block sequence start>'
Two sibling list items with different indentation — - run: build at two spaces, - run: test at three. The parser thinks the first list ended and something new began. This is the most common hand-edited GitHub Actions failure, because workflow files nest four levels deep and one list item drifts by a single space.
The same mistakes in go-yaml
Go programs that read YAML with go-yaml word the errors differently and add only a line number. I fed the same broken files to PyYAML 6.0.3 and go-yaml v3.0.1:
| Mistake | PyYAML | go-yaml v3 |
|---|---|---|
| Colon and space inside a value | mapping values are not allowed here | mapping values are not allowed in this context |
| Key indented differently from its siblings | expected <block end>, but found '<block mapping start>' | did not find expected key |
| List item indented differently | expected <block end>, but found '<block sequence start>' | did not find expected '-' indicator |
| Tab used for indentation | found character '\t' that cannot start any token | found character that cannot start any token, or found a tab character that violates indentation |
| Unclosed double quote | while scanning a quoted scalar … found unexpected end of stream | found unexpected end of stream |
Don't trust go-yaml's line number blindly. For a tab on line 3 it reported line 2; for a key mis-indented on line 5 it reported line 1, for a list item on line 3 it reported line 1, and for a colon on line 1 it gave no line at all. Look a few lines below the reported one, or run the file through a second parser to get a line and column.
The quiet failures: valid YAML, wrong data
These all parse without any error. We ran each one; the arrow shows what your program actually receives:
| You wrote | YAML delivers | Why |
|---|---|---|
country: NO | false | The Norway problem — YAML 1.1 reads NO/no/Yes/on/off as booleans |
answer: yes | true | Same boolean coercion |
version: 1.10 | 1.1 | Unquoted, so it's a float — trailing zero gone |
time: 12:30:45 | 45045 | Sexagesimal (base-60) notation: 12×3600+30×60+45 |
zip: 011234 | 4764 | Leading zero → octal |
port: 80 then port: 8080 | 8080 only | Duplicate keys: last silently wins, first is discarded |
The fix for the first five is the same: quote any scalar that must stay a string (country: "NO", version: "1.10"). The duplicate-key case is nastier — no quoting saves you, and a merge conflict resolved sloppily can leave two port: lines that both look right. A linter that flags duplicates is the only reliable catch.
Checking YAML before it reaches CI
- On a Mac, the fastest offline check is Python's parser:
python3 -c "import yaml,sys; yaml.safe_load(sys.stdin)" < config.yml— errors print with line and column.yamllint(via Homebrew) adds style checks and duplicate-key detection. - On an iPhone or iPad — reviewing a PR from your phone, editing a config over SSH — Yamlite does the same job on-device: paste, and errors appear with line and column instantly, including the duplicate keys and tab indentation cases above. Fully offline, nothing uploaded — which matters when the file you're validating is a production config.
- In a browser, online validators work but mean pasting your config — often containing secrets and internal hostnames — into someone else's server. For anything sensitive, keep validation local.
FAQ
Why does YAML turn NO into false? YAML 1.1 defines yes/no/on/off/true/false (any case) as booleans. Quote it ("NO") to keep the string. YAML 1.2 dropped this, but most parsers in the wild still speak 1.1.
Are duplicate keys an error in YAML? The spec says keys must be unique, but most parsers accept duplicates and keep the last one silently. Use a linter that flags them — the parser won't.
Why does my YAML fail with a tab error when I can't see any tabs? The tab is invisible. Re-indent the reported line with spaces, or run the file through an editor's convert-tabs command.
What's the fastest way to validate YAML on iPhone? An on-device validator like Yamlite — paste the file, read the line/column errors. No upload involved.
Config files travel with the rest of your toolchain — if you're batch-fixing filenames before a deploy, the same care-first instinct applies to batch renaming on Mac.