Common YAML Errors in Kubernetes and CI Pipelines (and How to Fix Them)
YAML powers the config layer of modern infrastructure — Kubernetes manifests, GitHub Actions workflows, docker-compose files, GitLab CI. Which means a single misplaced space can block a deployment at 6 pm on a Friday. The good news: YAML failures are highly repetitive. The same five or six mistakes account for nearly every broken pipeline. This guide catalogues them — with the exact error messages you will see in kubectl, Actions, and compose — and shows how to validate files before they ever reach production.
Error 1: Indentation Mistakes
The most common failure by far. One extra or missing space changes the structure of the document — sometimes producing a parse error, sometimes silently attaching a key to the wrong parent, which is worse because the file "works" but the value is ignored:
# BROKEN — resources is indented under image, not the container
spec:
containers:
- name: web
image: nginx:1.27
resources: # <- 2 spaces too deep
limits:
memory: 512Mi
# kubectl: error converting YAML to JSON: yaml: line 6:
# mapping values are not allowed in this context
# FIXED — resources aligns with name and image
- name: web
image: nginx:1.27
resources:
limits:
memory: 512MiThe silent variant: indenting env: at the pod level instead of the container level. Kubernetes ignores unknown fields in some paths, so your environment variables simply never appear. When a value "is not taking effect", check its indentation before anything else.
Error 2: Tab Characters
YAML forbids tabs in indentation, and the error message never says the word "tab":
yaml: line 12: found character that cannot start any token # Find the invisible culprit: grep -Pn "\t" deployment.yaml # Fix in place (Linux/macOS): sed -i 's/\t/ /g' deployment.yaml
Tabs sneak in when someone edits a file over SSH in a default vim, or copies a snippet from a terminal. Set your editor to insert spaces for .yml/.yamlfiles and add an .editorconfig so the whole team inherits the setting.
Error 3: Unquoted Strings Misparsed as Numbers and Booleans
YAML guesses types from unquoted values, and the guesses regularly break CI configs. Two famous cases: version numbers with trailing zeros become floats, and on/off/yes/no become booleans:
# GitHub Actions — the classic Python version bug
- uses: actions/setup-python@v5
with:
python-version: 3.10 # parsed as float 3.1 → installs 3.1!
python-version: "3.10" # correct — quoted string
# docker-compose — env values must be strings
environment:
DEBUG: no # → boolean false, then coerced to "false"
DEBUG: "no" # correct
# Kubernetes annotation — floats not allowed
metadata:
annotations:
version: 1.20 # error: expected string, got float
version: "1.20" # correctThe rule: quote anything that is a string but does not look like one — versions, phone numbers, PIN codes with leading zeros, country codes, and every yes/no/on/off value. Pasting the block into a YAML to JSON converter shows you instantly which type the parser actually produced.
Error 4: Multi-Document Separators (---)
A single YAML file can hold multiple documents separated by --- on its own line — the standard way to ship a Deployment and Service together. Two common failures:
- Missing separator — concatenating two manifests without
---merges them into one invalid document ("mapping key apiVersion already defined" or a schema error). - Indented separator —
---must start at column zero; indented, it becomes a plain string value.
apiVersion: apps/v1 kind: Deployment metadata: name: web # ... --- # column zero, its own line apiVersion: v1 kind: Service metadata: name: web-svc
Note for tooling authors: JSON.parse-style single-document parsers (like js-yaml's load) throw on multi-document files — use loadAll or split on the separator first.
Validate Before You Deploy: yamllint and kubectl --dry-run
Every error above is catchable before a commit ever triggers a pipeline. Layer these checks:
# 1. Lint syntax + style (tabs, indentation, duplicate keys) pip install yamllint yamllint deployment.yaml # 12:1 error syntax error: found character '\t' that cannot # start any token # 2. Validate against the Kubernetes schema — client side kubectl apply --dry-run=client -f deployment.yaml # 3. Validate against the LIVE API server (webhooks, quotas) kubectl apply --dry-run=server -f deployment.yaml # 4. docker-compose has its own validator docker compose config # parses, resolves, prints final YAML # 5. GitHub Actions — actionlint catches workflow-specific issues actionlint .github/workflows/*.yml
- yamllint catches pure YAML problems: tabs, bad indentation, duplicate keys, trailing spaces.
- kubectl --dry-run=client checks the manifest against resource schemas without touching the cluster; =server also runs admission control.
- docker compose config is the fastest way to see how compose interpreted your file, with variables resolved.
- Wire yamllint and actionlint into a pre-commit hook or a cheap CI job so broken YAML never reaches the expensive pipeline stages.
A 60-Second Debugging Checklist
- Read the line number in the error — the real mistake is usually on that line or the one above it.
- Check for tabs:
grep -Pn "\t" file.yaml. - Check sibling alignment around the reported line.
- Quote suspicious values: versions, on/off/yes/no, anything with a colon or leading zero.
- Confirm
---separators are at column zero. - Paste the file into a converter to see the parsed structure as JSON — misplaced nesting becomes obvious immediately.
Frequently Asked Questions
Run kubectl apply --dry-run=client -f file.yaml for a quick client-side syntax and schema check, or --dry-run=server to validate against the live API server including admission webhooks. Neither creates any resources. Add yamllint for style and indentation checks before that.
YAML implicitly typed one of your values: on, off, yes, and no become booleans, and version-like numbers such as 3.10 become floats (3.1). Quote the value — python-version: "3.10" — to keep it a string. The bare word on as a top-level workflow key is a known quirk GitHub handles specially.
It almost always means there is a tab character in your indentation. YAML forbids tabs for indentation. Find it with grep -P "\t" file.yaml, replace tabs with spaces, and set your editor to insert spaces for .yml files.