Why Kubernetes Rejects value: yes (YAML 1.1 vs 1.2)
By OpsAtlas · · 3 min read
Table of contents
You add a feature flag to a Deployment, run kubectl apply, and get this:
json: cannot unmarshal bool into Go struct field EnvVar.spec.template.spec.containers.env.value of type string
The manifest looks harmless:
env:
- name: FEATURE_SEARCH
value: yes
Nothing is wrong with the indentation, and the key is spelled correctly. The problem is that yes stopped being a string before Kubernetes ever saw it.
Two YAMLs in one ecosystem
YAML has two versions in common use, and they disagree about plain (unquoted) scalars:
- YAML 1.1 (2005) treats
yes,no,on,off,y,nand their capitalised forms as booleans. It reads0755as octal and22:22as a base-60 number. - YAML 1.2 (2009) keeps only
trueandfalseas booleans.yesis a string,0755is the decimal 755, and22:22is a string.
Kubernetes converts YAML to JSON before decoding it, and that conversion has historically followed 1.1 rules for booleans. So value: yes arrives as "value": true. EnvVar.value must be a string, so the API server rejects the request. The fix is to quote it:
value: "yes"
The same rule applies to every field that must be a string: env values, annotations, labels and ConfigMap data. A ConfigMap entry like ENABLED: on fails the same way.
Three more files that bite
GitHub Actions: the on: key
on:
push:
branches: [main]
GitHub’s own parser reads this correctly. But a Python script that loads the workflow with PyYAML, a YAML 1.1 parser, gets a dictionary whose key is the boolean True, not the string "on":
>>> yaml.safe_load(open(".github/workflows/ci.yml"))
{True: {'push': {'branches': ['main']}}, ...}
Linters, workflow generators and audit scripts written in Python run into this. They then report “no triggers defined” or crash with a KeyError: 'on'.
Docker Compose: ports below 60
The Compose documentation has long recommended writing port mappings as strings, and this is why:
ports:
- 22:22 # a 1.1 parser reads this as 1342 (22 × 60 + 22)
- "22:22" # always a string
Current Compose versions are stricter about this, but the file may also be read by other tools: linters, IDE plugins, scripts. Quoting costs nothing.
Ansible and anything Python-based
Ansible uses PyYAML. mode: 0644 without quotes is read as the octal number 420, which happens to be what you want. mode: 644 is the decimal number 644, which is a very different permission. Quote file modes as strings (mode: "0644") and the meaning cannot drift.
See what the parser saw
The fastest way to debug any of these is to stop looking at the YAML and look at the data it produces. Converting to JSON shows every type explicitly: a quoted "yes" and a bare true look different, and a key of true is obvious.
Paste the file into the YAML ⇄ JSON converter. It parses with YAML 1.2 rules, and it also lists every value that a 1.1 parser would read differently, with its line number. That warning list doubles as a checklist of what to quote. Since it runs in the browser, you can paste internal manifests without uploading them anywhere.
To see the YAML 1.1 behaviour on the command line, use PyYAML directly:
python3 -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin), default=str, indent=2))' < deploy.yaml
If that output differs from the converter’s output, you have found a value that depends on which parser reads the file.
Rules that prevent all of this
- Quote every string that could look like something else. That covers
yes/no/on/off, version numbers ("1.10"), anything with a colon, IDs with leading zeros and file modes. - Never rely on a bare boolean where the schema wants a string. Kubernetes env values, labels and annotations are always strings.
- Lint in CI.
yamllintwith thetruthyrule enabled flagsyes,onand the rest before they merge. - Validate the whole manifest, not just the syntax. Once the YAML parses, run it through the Kubernetes YAML validator to catch the problems that come next: selectors that match nothing, missing Secrets, and insecure defaults.
YAML is a convenient format to write, but parsers fill in the types and they don’t all agree. Quote anything that could be read two ways.
Try it yourself
YAML ⇄ JSON converter
Convert YAML to JSON and back with exact error positions, anchor and merge-key expansion, and warnings for YAML 1.1 gotchas like the Norway problem.
KubernetesKubernetes YAML validator and visualizer
Paste Kubernetes manifests to get a resource diagram and a security and best-practice review: privileged pods, root, probes, limits, deprecated APIs.
KubernetesBeta