改 docker-compose.yml 加了一行环境变量,docker compose up 直接报:

yaml: line 12: did not find expected key

第 12 行看着完全正常。YAML 的报错行号经常指向「发现异常的位置」,而不是「写错的位置」——真正的问题可能在上面好几行。

下面这些结论都在 SnakeYAML 2.6(Spring Boot 默认的 YAML 解析器)上逐条跑过,报错文本就是实际输出。

坑一:Tab 缩进,编辑器看不出来

输入第 2 行开头是一个 Tab:

server:
	port: 8080

实际报错:

第 2 行第 1 列(found character '\t(TAB)' that cannot start any token)

YAML 规范只允许空格缩进。Tab 在编辑器里通常渲染成 4 个空格的宽度,肉眼完全看不出区别,这是它最坑的地方。

定位方法:

grep -Pn '\t' application.yml

Windows PowerShell:

Select-String -Pattern "`t" -Path application.yml

修复:编辑器里开启「显示空白字符」并把 Tab 转成空格,或者直接替换:

sed -i 's/\t/  /g' application.yml

坑二:同级缩进差一个空格,报错指向下一行

a:
  b: 1
 c: 2

实际报错:

第 3 行第 2 列(expected <block end>, but found '<block mapping start>')

b 缩进 2 个空格,c 缩进 1 个空格,解析器认为 c 要开启一个新的层级,但上一层还没结束。

YAML 并没有规定「必须缩进 2 个空格」,2 个、4 个都行,但同一层级必须完全一致。整份文件选定一种宽度后不要中途换。从别处复制一段配置进来时,最容易出现这种混搭。

坑三:布尔值和数字被悄悄改了类型

这一类最容易被忽略:文件能解析,不报错,但程序读出来的值已经不是你以为的值了。以下都是 SnakeYAML 2.6 的实测解析结果:

输入写法 解析后的实际值
flag: yes 布尔 true
flag: no 布尔 false
flag: on / off true / false
version: 1.10 浮点数 1.1(尾零丢失)
start: 12:30 整数 750(六十进制)
zip: 0123 整数 83(八进制)
zip: "08001" 字符串 08001(加引号才保险)

注意:格式化工具会保留 yes、1.10 的原始写法(这是对的,不该乱改配置),但解析器读取时类型已经变了——Spring Boot 的 @Value、Docker Compose 读到的就是上表右边的值。

几个解释:

  • yes / no / on / off 是 YAML 1.1 的布尔字面量,只有 YAML 1.2 才取消。Spring Boot、Docker Compose 这类配置的底层解析大多是 1.1 风格,所以它们会被当成布尔。
  • 12:30 在 YAML 1.1 里是按六十进制解析的数字,不是时间字符串。想要时间,必须加引号。
  • 前导 0 表示八进制,0123 等于十进制的 83。邮编、工号、手机号这类一律加引号。
  • 版本号 1.10 被当成浮点数,尾零直接丢掉。版本号一定要加引号。

规则可以简化成一句:除了明确的数字和布尔值,其他一律加引号。加引号不会错,不加才可能错。

附加一:冒号后面必须有空格

a:b: 1

这里的键是 a:b,不是 a 下面的 b。YAML 的键值分隔符是「冒号 + 至少一个空格」,只写冒号不分割,实测格式化后原样输出 a:b: 1。

写成 a: b: c 这种一行两个冒号的,会直接报:

第 1 行第 5 列(mapping values are not allowed here)

附加二:重复键默认不报错

server:
  port: 8080
server:
  port: 9090

开启重复键检测后报「第 3 行第 1 列(found duplicate key server)」。但很多解析器默认是「后者覆盖前者」,静默生效、不吭声。复制粘贴配置段时最容易出这种问题——线上到底跑的是哪个值,只能靠猜。

建议在提交前用带重复键检测的工具校验一遍,别等上线才发现。

排查顺序

遇到 YAML 报错,按这个顺序看,比盯着报错行有效:

  1. 有没有 Tab:grep -Pn '\t' 文件
  2. 同级缩进是否一致
  3. 值要不要加引号(版本号、时间、以 0 开头的编号)
  4. 有没有重复键
  5. 还是定位不到,就用工具校验,会给出准确的行号和列号

工具的两个使用要点

用 YAML 格式化/校验工具 处理时要知道:

  • 注释会完整保留。文档头注释、键上方的说明、行尾备注都会跟着键走,按键名排序时也不会丢;yes、1.10 这类写法保持原样,不会被改成 true、1.1。
  • 多文档(--- 分隔)一次只处理第一份,有多个文档要拆开逐个处理。

需要 JSON 和 YAML 互转,用 JSON ⇄ YAML;写 Compose 配置时对照 Docker Compose 速查 更省事。挂载类问题可以看旧文 Docker Compose 挂载后容器里文件没了。