改 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 报错,按这个顺序看,比盯着报错行有效:
- 有没有 Tab:
grep -Pn '\t' 文件 - 同级缩进是否一致
- 值要不要加引号(版本号、时间、以 0 开头的编号)
- 有没有重复键
- 还是定位不到,就用工具校验,会给出准确的行号和列号
工具的两个使用要点
用 YAML 格式化/校验工具 处理时要知道:
- 注释会完整保留。文档头注释、键上方的说明、行尾备注都会跟着键走,按键名排序时也不会丢;
yes、1.10这类写法保持原样,不会被改成true、1.1。 - 多文档(
---分隔)一次只处理第一份,有多个文档要拆开逐个处理。
需要 JSON 和 YAML 互转,用 JSON ⇄ YAML;写 Compose 配置时对照 Docker Compose 速查 更省事。挂载类问题可以看旧文 Docker Compose 挂载后容器里文件没了。