A `helm upgrade` fails with `error converting YAML to JSON: yaml: line 41: did not find expected key`. How do you find the cause?
answer
- The number counts something you have not opened
- Helm names the file, not the line's home
- Parsers report where they gave up
- Ten namespaces pass, two fail
- One key hides a double indent
basics
~20 sThe line number indexes the rendered manifest, not the template file Helm names in the same message. Render the chart with the failing release's values, count to that line, and look at it and the line above for a value spliced in at an indentation that does not match its siblings.
solid answer
~60 sThat error is a parse failure, raised before anything reaches the cluster: Helm rendered the templates, then failed to read the result as YAML. The full message names the template file, which is genuinely useful, but the line number belongs to that file's *rendered output*, so opening the template and counting to line 41 finds the wrong thing. Render the chart with the same values the failing release uses, count to the reported line, and read it together with the line above — a parser reports where it gave up, which is typically the line after the damage. The whitespace causes are a short list: a multi-line value spliced with `indent` after the template's own literal spaces, so its first key is deeper than the rest; a trailing `-}}` that pulled the following line up; or a block placed at a depth that does not match the keys around it. If the same chart renders in most environments and fails in one, the trigger is in that environment's values, not the template.
code
yaml · 2 linesannotations:
{{ toYaml .Values.podAnnotations | indent 4 }}go deeper
Learn to read the message as two parts: a file name you can trust and a line number that belongs to the rendered output. Knowing not to count lines in the template already saves you an afternoon.
Be ready to name the whitespace defects that produce this class of error and to explain why the parser typically flags the line after the one that is actually wrong.
The judgement being tested is the per-release angle: recognising that a template can be latently broken, that values decide whether it surfaces, and that CI should render every environment's values rather than only the chart defaults.
Own the failure class rather than the incident — how a fleet of environments is exercised before an upgrade, and whether a chart should splice arbitrary user-shaped structure at all when the failure lands at upgrade time in production.
## What the message is telling you Helm's full text is roughly `YAML parse error on tile-server/templates/configmap.yaml: error converting YAML to JSON: yaml: line 41: did not find expected key`. Three facts are packed in there. First, this is a *render-time* failure. Helm executed the templates and then tried to parse the output as YAML so it could turn it into API objects. It never got as far as talking to the cluster, so nothing was applied, no release revision was created, and the previous revision is untouched. Second, the file name is the template that produced the offending document, and that part is reliable — it narrows the search to one file. Third, and this is the part that costs people twenty minutes: the line number is a line in the *rendered output* of that template, not a line in the template source. The two files rarely have the same length, because control-flow lines vanish, conditionals drop whole blocks and a single splice action expands into many lines. Opening the template and jumping to line 41 lands somewhere arbitrary. ## The method Render the chart offline with exactly the values the failing release uses — the same value files and the same overrides — and count to the reported line in that output. Then read that line together with the one above it. A YAML parser reports the position at which it could no longer make sense of the document, and for indentation defects that is usually the line *after* the one that is actually wrong: the first key of a spliced block lands too deep and parses as part of the previous value, and only the second key raises the alarm. Once you are looking at the right two lines, map them back to the action that emitted them and check the splice. ## The whitespace causes worth checking, in order The most common is `indent` used where `nindent` was needed. The action sits after literal spaces in the template, those spaces are emitted, and `indent` adds its own on top — so the block's first key is deeper than the keys beneath it. The rendered evidence is unmistakable: one key at, say, eight spaces followed by siblings at four. The second is an over-eager trailing trim marker. A `-}}` at the end of a line deletes the newline that terminated it, so the next line's content is appended to the current one and you get something like `name: tileslabels:` — a line the template plainly does not contain. The third is a block placed at a depth that simply does not match its neighbours, most often after a chart is refactored and a partial is inlined at a different level than the one it was written for. ## Why it fails in some environments and not others This is the detail that turns the question into a senior one. Consider a chart that wraps a vendor image for a geospatial tile server and renders a values-driven config, deployed across a 12-namespace fleet from one chart and twelve values files. Ten namespaces upgrade cleanly; two fail with this error. The template is identically broken in all twelve. It splices annotations with `indent` after literal indentation, so the first emitted key is always too deep. But in ten of the namespaces the value has exactly one key, and a *single* line that is merely deeper than its parent requires is perfectly good YAML — extra depth under a key carries no meaning. The document parses and the release works. In the two namespaces where someone added a second annotation, the first key is at eight and the second at four, the mapping is inconsistent, and the parse fails. The general lesson: when a render breaks for some releases and not others, the difference is in the values, and the template defect has been latent the whole time. Diffing the failing values file against a working one usually points straight at the block. ## The fix and the guardrail The fix is the standard idiom — a leading trim marker plus the newline-emitting indent function, so the block's own depth is set once and applies to every line regardless of how many lines the value produces. The guardrail is to render every environment's values in CI rather than only the defaults. A chart that is exercised only with `values.yaml` will keep shipping defects that need a two-key map to expose, and the first place they surface is an upgrade of a live release. ## What this error is not It is not the cluster rejecting your object; a rejection comes back from the API server and names a field, not a line. It is not a problem with the values file's own YAML either — that fails earlier, while reading the file, with a message about the file you passed. And it is not a template execution error such as a missing function or an unclosed action; those fail before any output exists to parse.
- Why can the same chart render fine in ten namespaces and fail in two?Because a one-line spliced value hides a double-indent. A single key that sits deeper than its parent requires is still valid YAML, so the defect is invisible; the moment a values file supplies a second key, the first sits deeper than the second and the mapping becomes inconsistent. The template was wrong everywhere, and only some values expose it.
- Helm names a template file in that error — why is the line number still not a template line?Because the parse runs against that template's rendered output. Control-flow lines vanish, conditionals remove whole blocks and one splice expands into many lines, so the two files have different lengths and different content. You have to render with the failing release's values and count in the output.
- How would you tell this apart from the cluster rejecting the object?By where the message comes from and what it names. A parse failure names a template file and a line, happens before any request is made, and leaves the previous release revision untouched. A rejection comes back from the API server, names a field or an object, and only happens after the document parsed cleanly.
saying these in an interview costs you the question
- Opens the template file and counts to the reported line
- Blames the values file's own YAML syntax
- Says the message means the cluster rejected the object
- Adds quotes or block scalars until it parses
- Assumes the chart is broken for every release equally
- Thinks Helm validated against the API server at this point