Config Formats

YAML Gotchas: Type Coercion, Indentation and Multiline Strings

YAML files that "look right but run wrong" usually fail in three places: silently coerced scalars, misplaced indentation, and ambiguous multiline strings. This guide dissects each trap with detection methods and a toolchain.

By 巧匠 Team·8 min read·Updated 2026-08-31

Type coercion: the biggest silent bomb

YAML infers scalar types automatically, and many values that "look like strings" get silently converted: `no`/`na`/`n` become the boolean false (the famous Norway problem), `on`/`off`/`yes` become true/false, `0733` becomes octal, and `1.10` becomes the number 1.1.

The result is config that "looks right but runs wrong": version numbers lose precision, country codes turn into booleans. Unless you explicitly want a number/boolean/null, quote every scalar — the single most stress-reducing YAML rule.

country: no          # 实际是 false,不是挪威!
country: "no"       # 这才是字符串
version: 1.10       # 变成数字 1.1
version: "1.10"    # 字符串,安全

When quoting is mandatory

Three classes of values must be quoted: ① anything that could be coerced to a number/boolean/null (`no`, `on`, `1.10`, plus `2026-01-01`, which parses as a date object); ② values starting with special characters (`*` `&` `!` `%` `@`); ③ values containing "colon + space" or ` #`.

The date trap deserves emphasis: `date: 2026-01-01` is a date object in most parsers, not a string, and it surfaces in a totally different shape during log debugging. Write `date: "2026-01-01"` for a string.

date: 2026-01-01      # 日期对象,不是字符串
date: "2026-01-01"   # 字符串
value: a: b           # 语法错误(冒号+空格)
value: "a: b"        # 正确

Indentation: one level off, data lands elsewhere

YAML accepts spaces only, never tabs (a tab is a hard syntax error); sibling keys must align; content after a list marker `- ` belongs to a child level. One level off and data silently lands under the wrong node — most parsers never complain.

Two frequent details: a space must follow the colon (`key:value` is invalid), and a comment `#` needs a preceding space or it becomes part of the value. A YAML language server (e.g. Red Hat YAML for VSCode) flags these live.

server:
  host: 0.0.0.0     # 缩进 2 空格,属于 server
  ports:
    - 80            # 列表项再缩进一级
    - 443
# key:value 错 → key: value 对

Multiline strings: | vs >

The pipe `|` keeps newlines (literal block); the fold `>` collapses newlines into spaces (folded block); adding `-` strips the trailing newline (`|-`, `>-`). Use `|` for scripts, certificates and SQL; use `>` for long prose.

The indentation baseline is set by the first line: every line in the block must be indented deeper than the parent key and consistently — this is exactly why "it looks aligned but still errors": you compared against the wrong level.

script: |
  #!/usr/bin/env bash
  set -euo pipefail
  echo "ok"
desc: >
  这是一段很长的说明文字,
  换行会被折叠成空格。
sql: |-
  SELECT *
  FROM users;

Anchors, aliases and merge keys

An anchor `&name` marks a node, an alias `*name` references it, and `<<:` merges a mapping into the current key — this is how docker-compose reuses shared config.

Two limits to remember: lists cannot be merged with `<<`, only referenced whole; an alias is a reference, not a copy, so changing one place changes all. Bonus trap: unquoted `80:80` parses as a sexagesimal (base-60) number under YAML 1.1 — always quote port mappings as `"80:80"` in compose files.

common: &common
  image: nginx:1.27
  restart: unless-stopped
web:
  <<: *common
  ports:
    - "80:80"

Debugging toolchain and checklist

Validate before shipping: yamllint for style and structure, the consuming tool itself (`docker compose config`, `kubectl --dry-run`), or a one-liner `yaml.safe_load` in Python to confirm what the parser actually sees.

Debugging principle: the reported line usually marks where the problem surfaced, while the real mistake sits a few lines above in indentation or quoting. Checklist: quote scalars, spaces only, space after the colon, pick the right `|`/`>` block, quote port mappings, run a parser before committing.

yamllint config.yaml
docker compose config        # 解析并规范化输出
kubectl apply --dry-run=client -f manifest.yaml
python -c "import yaml,sys; yaml.safe_load(open(sys.argv[1]))" config.yaml