YAML "did not find expected key": It’s Indentation, and the Line Number Is Usually Wrong
Every case that produced this message came down to the same thing: a key sitting at an indentation level that does not line up with the block it is in. The harder part is that the line number you were given may point somewhere that looks perfectly fine.
The short answer
did not find expected key means a mapping key is indented to a column that matches no open block. Every key inside one mapping has to start at the same column, and one of yours does not.
The second half matters as much as the first: the line number you were given is probably not where the mistake is. Across the cases measured here, go-yaml — the parser behind kubectl, Helm, Docker Compose, and yq — named an earlier line every time, because it reports where the block it could not finish began rather than the key that broke it.
The smallest example
One space. That is the whole bug:
parent:
a: 1
b: 2 # one space, not twob is indented one space, a two. They are supposed to be siblings inside parent, so the parser reaches b, finds it does not belong to the block it is reading and does not close it either, and gives up.
parent:
a: 1
b: 2 # now level with aWhy the reported line looks innocent
A YAML parser reading a block mapping does not know anything is wrong until it meets a line that neither continues the block nor ends it. By then it has been reading since the block started — and in go-yaml, the line it names is where that reading began.
The clearest case measured: a four-line document whose mistake is on line 4, reported as line 1.
top:
mid:
leaf: 1
other: 2 # <- the mistake is here, on line 4go-yaml yaml: line 1: did not find expected key
libyaml did not find expected key (line 4, column 4)
yaml 2.9.0 All mapping items must start at the same column (line 4, column 1)
# Line 1 is "top:" — nothing is wrong with it.So a reader who trusts the number inspects top:, finds nothing wrong, and concludes the message is nonsense. It is not — it is describing where the parser was, not where you erred. The maintainers of yq have an open issue describing this wording as misleading for exactly this reason.
Finding the actual mistake
- 1. Treat the reported line as a starting point, not the answerIn the cases measured here the reported line was where the unfinished block began, so it often names a key that is entirely correct. Read downward from it rather than staring at it.
- 2. Find the first key below it whose indentation does not match its siblingsEvery key inside one mapping must start at the same column. A single space too few or too many is enough, and it is invisible in most editors unless whitespace rendering is on.
- 3. Check the keys that follow a block scalar or an anchored blockTwo of the four measured cases involved a key placed after a block scalar or an anchor. Those regions push the eye out of alignment, and the parser only notices when the next key arrives.
- 4. Rule out tabs before assuming it is alignmentA tab produces a different message entirely, so if you are seeing this one, the indentation is spaces and the count is wrong. Turning on whitespace display settles it quickly.
- 5. Re-parse after the correctionIndentation mistakes often come in pairs, because whatever produced one line usually produced its neighbours. Parsing again is faster than re-reading the block.
What each parser reports
28 malformed documents were tried; 4 produced this message, and all 4 were the same root cause. Here is where each parser said the problem was, against where it actually was:
| Cause | Mistake on | go-yaml | libyaml | yaml 2.9.0 |
|---|---|---|---|---|
| A sibling key indented one space less than the key above it | line 3 | line 2 | line 3 | line 3, col 1 |
| A key dedented to a column that matches no open block | line 4 | line 1 | line 4 | line 4, col 1 |
| A key after an anchored block, indented inconsistently | line 3 | line 2 | line 3 | line 3, col 1 |
| A key after a block scalar, indented inconsistently | line 4 | line 3 | line 4 | line 4, col 1 |
go-yaml pointed earlier than the mistake in 4 of 4 cases, by as much as 3 lines. libyaml named the right line in all of them. These are observations of the versions listed at the end, not a statement about every parser or every release — but the pattern is consistent enough to be worth knowing which family produced your message.
How it reaches you
The same string arrives wrapped differently depending on what you ran. None of these is a separate problem — the inner message is the one that identifies the cause:
| Tool | Appears as |
|---|---|
| kubectl | error converting YAML to JSON: yaml: line N: did not find expected key |
| Helm | YAML parse error on chart/templates/x.yaml: error converting YAML to JSON: yaml: line N: … |
| Docker Compose | yaml: line N: did not find expected key |
| yq | Error: yaml: line N: did not find expected key |
| Home Assistant, Elastic | did not find expected key while parsing a block mapping |
The fix is the same in all of them, because the problem is the YAML rather than the tool. For Helm specifically, the line refers to the rendered output rather than the template, so render first and count lines there.
The Python wording for the same mistake
PyYAML can produce two different messages for identical input, depending on which scanner it was built with. The C scanner reports did not find expected key; the pure-Python one reports expected <block end>, but found '<block mapping start>':
CSafeLoader (libyaml) did not find expected key
SafeLoader (python) expected <block end>, but found '<block mapping start>'Both name the same line and column, and both mean what this page describes. If you are searching for the second phrasing and landed here, you are in the right place.
Checking the file
A second parser is genuinely useful here, because the first one told you the wrong line. Pasting the document into the YAML Formatter gives a different reading of the same file — in the cases measured above it reported the offending key's own line and column, and described the problem as All mapping items must start at the same column, which names the cause rather than the parser's state.
It does not repair indentation, and it does not check Kubernetes resources, Helm charts, Docker Compose schemas, or anything else about what your configuration means — only whether the YAML parses. Once it does, the formatter will lay the document out, which is a quick way to confirm the block structure is what you intended. Parsing runs in your browser, so a file containing real data is not uploaded anywhere.
If your message is different
These symptoms look similar but produce other messages, and the fixes are not the same. None of them causes did not find expected key:
| Symptom | You get instead |
|---|---|
| A tab used for indentation | found character that cannot start any token |
| Missing space after a colon | mapping values are not allowed in this context |
| An unquoted value containing a colon | mapping values are not allowed in this context |
| An unterminated quote | found unexpected end of stream |
If you arrived via kubectl, the wrapper around all of these is the same, and error converting YAML to JSON covers which layer failed and how those other line numbers behave — including cases where the reported line lands past the mistake rather than before it.
Versions measured
Error wording and reported positions are implementation details and change between releases. The strings and line numbers on this page were produced by:
- go-yaml (via kubectl kustomize) —
kubectl v1.36.1 - libyaml (PyYAML CSafeLoader) —
PyYAML 6.0.3 - PyYAML pure-Python SafeLoader —
PyYAML 6.0.3 - DataToolsHQ YAML Formatter —
yaml (npm) 2.9.0
The rule underneath does not change: every key in a mapping starts at the same column, in every parser and every version.