kubectl "error converting YAML to JSON": Which Layer Failed, and What the Line Number Means
This message comes from the YAML parsing step that runs before Kubernetes decodes your manifest into an object. That narrows the cause to syntax — and means the reported line number marks where parsing became impossible, which is not always where the mistake is.
The short answer
Your manifest never became a Kubernetes object. The YAML was converted to JSON first, that conversion failed, and everything Kubernetes would check — field names, required fields, API versions, types — happens after this point and reports different errors.
So this is always a syntax problem. A misspelled field such as valueFROM, a wrong apiVersion, or a missing required key cannot produce this message. If you are hunting for one of those, you are looking at the wrong layer.
The second thing to know is that the line number marks where parsing became impossible, not necessarily where you made the mistake. Across 8 measured parse failures the reported line matched the mistake exactly 5 times, pointed earlier 1 time, and pointed later 2 times — once at a line number past the end of the file.
Why it mentions JSON when you wrote YAML
Kubernetes tooling does not consume YAML directly. The file is parsed as YAML and converted to JSON by a library — sigs.k8s.io/yaml — and only the JSON is handed onward.
That library is where the message originates. Calling YAMLToJSON() from sigs.k8s.io/yaml v1.4.0 on the same files reproduced the errors kubectl printed character-for-character, including the line numbers; kubectl only adds the surrounding error parsing <file>:context. Saying “kubectl converts YAML to JSON internally” is directionally right but hides the useful part: the failure comes from a YAML parser, so only YAML syntax can cause it.
Which layer actually failed
Three separate stages can reject a manifest, and only the first produces this error.
| Stage | What it does | Typical failure | This error? |
|---|---|---|---|
| A. YAML parse & YAML-to-JSON conversion | Reads the file as YAML and converts it to JSON before Kubernetes sees it. | error converting YAML to JSON: yaml: line N: … | Yes |
| B. JSON to Kubernetes object decoding | Maps the JSON onto a typed Kubernetes object such as a Deployment. | cannot be handled as a Deployment; strict decoding error: unknown field … | No |
| C. Schema and API validation | Checks the object against the cluster’s OpenAPI schema. Needs a server. | error validating data: …; failed to download openapi: … | No |
A manifest with a duplicate key demonstrated the split: it passed stage A cleanly and failed later, at validation. Stage C needs a cluster, which is why an offline --dry-run=client can catch a syntax error but cannot tell you whether Kubernetes would accept the object.
What the reported line number means
The parser reports the point at which it could no longer continue. When the offending character is illegal where it appears, that is exactly your mistake. When the mistake produces something legal but different, the failure surfaces elsewhere.
| Mistake | On line | Reported | Why |
|---|---|---|---|
| A tab used for indentation | 4 | 4 | A tab cannot legally start an indented line, so the scanner fails on the character itself. |
| An unquoted value containing a colon and a space | 6 | 6 | The second colon is illegal where it appears, so the failure and the mistake coincide. |
| A missing space after a colon, written as a:b | 6 | 7 | a:b is a valid plain scalar, not a mapping. Nothing is wrong until the next line tries to follow a scalar with another key. |
| A double-quoted value never closed | 6 | 8 (file has 7) | The open quote swallows the rest of the file, so parsing only becomes impossible at the end — reported past the last line. |
The clearest case is an unterminated quote. The open quote consumes the rest of the file, so parsing only fails at the end:
apiVersion: v1
kind: ConfigMap
metadata:
name: cfg
data:
a: "unterminated
b: fine$ kubectl apply --dry-run=client -f f6-quote.yaml
error: error parsing f6-quote.yaml: error converting YAML to JSON: \
yaml: line 8: found unexpected end of stream
# The file is 7 lines long.A line number that cannot exist is a strong hint: look for something opened and never closed — a quote, a bracket, or a brace — starting anywhere above.
Why Helm can point at a line that looks correct
Helm renders templates before parsing the result. It names the source template in the error, but the line number belongs to the rendered document, and template expansion moves everything below it.
apiVersion: v1
kind: ConfigMap
metadata:
name: cfg
data:
{{- range .Values.extra }}
item-{{ . }}: ok
{{- end }}
greeting: {{ .Values.greeting }} # <- line 9 of this file$ helm template ./chart
Error: YAML parse error on probe2/templates/cm.yaml: \
error converting YAML to JSON: yaml: line 12: mapping values are not allowed in this context
# The template file has 9 lines. With six items in .Values.extra,
# the rendered document is 12 lines — and 12 is the rendered line.The offset is not fixed: it is however much your templates expanded above the fault. The same fixture with three list items instead of six reported the line with no offset at all. Running kubectl against that rendered output reported the same line, confirming the number describes rendered content rather than your file.
So the useful step is to render first — helm template ./chart — and count lines in that output. On the fixtures tested here helm lint and helm template reported the same diagnosis and the same line, differing only in wording, so either is fine for locating the fault. Install and upgrade paths were not tested.
Duplicate keys: a mistake this layer may not catch
Not every YAML problem produces an error. A repeated key in the same mapping converted without complaint in the version tested, keeping the last value and discarding the first:
data:
a: one
a: twosigs.k8s.io/yaml v1.4.0 YAMLToJSON():
{"apiVersion":"v1","data":{"a":"two"},"kind":"ConfigMap", … }
# no error, no warning — the first value is gone
DataToolsHQ YAML Formatter (yaml 2.9.0):
Map keys must be unique at line 7, column 3Nothing warns you. In a long ConfigMap or a merged patch, a key defined twice silently resolves to whichever came last. This is worth knowing precisely because it is the failure mode this error message will never report — measured on sigs.k8s.io/yaml v1.4.0; other parsers and versions differ, which is the whole point of the next section.
A debugging sequence that respects the layers
- 1. Confirm it is a syntax failure, not a Kubernetes oneIf the message contains "error converting YAML to JSON", stop looking at field names, apiVersion values, and required fields. Those are checked later and fail with different messages.
- 2. Read the reported line, then read the lines above itThe number marks where parsing became impossible. For unterminated quotes and brackets that can be well past the mistake — and, as measured, even past the end of the file.
- 3. For Helm, render before you debughelm template writes out the document Helm actually parsed. The reported line refers to that rendered output, so compare it against the render rather than the template source.
- 4. Get a second parser opinion on the syntaxA different YAML implementation will phrase the problem differently and may point at a different line or column. Disagreement between two parsers is itself a useful signal about where the structure breaks.
- 5. Only then validate it as a Kubernetes objectOnce the file parses, kubectl --dry-run=server or a schema validator such as kubeconform checks fields, types, and API versions. Client-side parsing does not do this.
Getting a second parser opinion
Different YAML implementations describe the same broken file differently. Running the nine fixtures from this article through the YAML Formatter — which uses the yamlpackage, version 2.9.0, not Go's parser — produced a different set of messages:
| Problem | kubectl | YAML Formatter |
|---|---|---|
| Tab used as indentation | found character that cannot start any token (line 4) | Tabs are not allowed as indentation (line 4, col 1) |
| Duplicate key in a mapping | accepted — later value kept, no message | Map keys must be unique (line 7, col 3) |
| Unterminated double quote | found unexpected end of stream (line 8, in a 7-line file) | Missing closing " quote (line 7, col 10) |
| Missing space after a colon | mapping values are not allowed in this context (line 7) | Implicit keys need to be on a single line (line 6, col 3) |
Two useful differences: it names the category of problem rather than the parser state, and it reports a column as well as a line. It also rejected the duplicate key that the conversion layer accepted.
The limits matter as much. This is a different parser, so its line numbers will not always agree with kubectl's, and neither one is authoritative about the other. It checks YAML syntax only: it does not validate Kubernetes schemas, field names, or API versions, does not render Helm templates, and cannot tell you whether a cluster will accept your manifest. For that, use --dry-run=server or a schema validator such as kubeconform. Parsing runs in your browser, so a manifest containing internal hostnames is not uploaded anywhere.
Versions measured
Error strings and line numbers are implementation behaviour, not a YAML specification. Everything above was measured on:
- kubectl (client) —
v1.36.1 - sigs.k8s.io/yaml —
v1.4.0 - Helm —
v4.2.3 - DataToolsHQ YAML Formatter —
yaml (npm) 2.9.0 - Docker Engine —
29.6.2
A future kubectl or Helm bundling a different YAML library may word these differently or number them differently. The layer distinction is structural and should outlast the strings.