先理解错误:YAML 能读懂,不代表配置结构正确

yaml: unmarshal errors 通常表示程序正在把 YAML 文本转换成内部配置结构时,某个键或值无法放入预期字段。它与“文件完全不是 YAML”相关,但并不相同:有些文件在 YAML 语法层面可以解析,却把列表写成了字符串、把对象写成了布尔值,进入 Mihomo 配置结构时才失败。

排查时先把问题分为两层。第一层是 YAML 语法与层级,例如 Tab、缩进、冒号后缺空格、引号没有闭合;第二层是 Mihomo 模式,例如字段名不存在、字段所属层级错误、值类型不符合文档。Mihomo YAML 语法页明确强调大小写敏感、空格缩进、同级左对齐、冒号后空格和数组的短横线。配置参考则用来确认具体字段结构。

两个验证问题:“这是一份合法 YAML 吗?”和“这是一份符合当前 Mihomo 版本的配置吗?”必须分别回答。普通 YAML 检查器通过,只能回答第一个问题。

不要看到 unmarshal 就删除整个订阅或重装客户端。先复制原文件,记录报错全文、使用的客户端和核心版本、配置来源以及最近一次修改。若错误在订阅更新后出现,保留更新前副本;若是手工编辑后出现,只回看这次改动。最小差异比重新拼一份大型配置更容易确认。

从第一条行号开始,连同父级和前一段一起检查

错误可能写成 line 42: cannot unmarshal ...,也可能列出多行。先处理第一条,因为前面的层级错误经常让后续字段全部落入错误位置,产生一串次生提示。行号通常指解析器确认矛盾的位置,不保证真正的笔误就在那一行;未闭合引号、上一段少缩进或错误列表起点可能提前数行。

  1. 复制原配置为只读备份,不在唯一副本上继续试错。
  2. 使用显示行号、空白字符和 UTF-8 的纯文本编辑器打开文件。
  3. 定位第一条报错行,再向上查看同一父级和前一个列表项。
  4. 把相关小段与官方字段示例比较,暂时不要格式化整份文件。
  5. 只修改一个确定问题,保存为新副本并重新验证。

如果报错行包含敏感节点,不要把整行直接发到公开讨论区。可以保留字段名和数据类型,把服务器、端口以外的凭据、UUID、密码、订阅参数替换为占位符。脱敏后仍应保留空格数量和短横线位置,因为这些正是排查层级所需的信息。

错误片段优先含义查看范围
did not find expected key键、引号或前一层语法中断当前行及上方数行
cannot unmarshal !!seq into string给字符串字段传了列表字段文档与短横线
cannot unmarshal !!map into bool给布尔字段传了对象缩进与预期值类型
field ... not found未知字段、拼写或版本差异当前核心配置参考

编辑器自动折行不会改变实际行号,但复制到聊天工具可能把 Tab 转成空格或反过来。最好直接在原始纯文本副本中开启不可见字符,再截取一个最小、已脱敏的代码块。

先修缩进、Tab、冒号和引号这些语法问题

YAML 用缩进表达父子关系,不能像某些编程语言那样用括号补救。官方语法说明要求用空格而不是 Tab,同一层级左侧对齐,键后的冒号紧跟一个空格,数组项以同层短横线开始。具体使用两个还是四个空格可以由文件风格决定,但同一层必须一致。

下面的坏例子在列表项前使用了 Tab,并让 type 与 name 不在同一层。编辑器看起来可能接近对齐,解析器看到的结构却不同。

proxies:
	- name: Example
    type: ss

改成纯空格并让同一代理对象的键对齐:

proxies:
  - name: Example
    type: ss

冒号也要区分语法与字符串内容。mixed-port: 7890 中冒号后应有空格;URL 或 IPv6 等包含冒号的值在存在歧义时应按文档使用引号。注释符 # 若属于值的一部分也需要引用,否则后方可能被当作注释。引号必须成对,智能引号不能代替 ASCII 单引号或双引号。

不要全局替换所有空格:自动“修复缩进”可能改变多行脚本、规则内容或字符串。只处理已确认的结构区域,并保留修改前副本。

YAML 1.2.2 规范适合确认通用语法,但 Mihomo 文档中的字段示例更接近实际配置。先让结构成为合法 YAML,再进入字段类型检查。

语法通过后,检查字符串、数字、布尔值、对象和列表

当错误明确写出 cannot unmarshal,注意其中的源类型与目标类型。!!seq 表示序列,也就是列表;!!map 表示键值对象;!!str 是字符串;整数和布尔值也有各自类型。错误不是要求你机械删除短横线,而是提示当前字段收到的结构与核心定义不一致。

例如端口通常应是数字,开关应是布尔值。下面把端口写成带引号字符串,又把布尔开关写成列表:

mixed-port: "7890"
allow-lan:
  - true

按配置参考确认字段后,结构应类似:

mixed-port: 7890
allow-lan: true

相反,proxies、proxy-groups 和 rules 本来就常是列表,不能为了消除 !!seq 而统一改成一行字符串。每个字段必须单独对照当前版本的文档。字段名还区分大小写和连字符;从其他客户端、旧内核或订阅转换器复制的键,可能在当前版本不存在或位于不同层级。

  • unknown field:先查拼写、大小写、版本和所属父级。
  • sequence into scalar:确认该字段究竟只接收一个值还是多个项目。
  • map into scalar:检查多缩进的一层是否意外把后续键变成对象。
  • 重复键:不要依赖解析器覆盖前值;合并成一个明确字段。
  • null 或空值:按文档决定删除字段、填入有效值或保留空字符串。

如果错误来自远程订阅,不要长期直接改客户端下载的缓存,因为下次更新会覆盖。先确认服务商提供的是适用于 Mihomo 的格式;需要本地覆写时,使用客户端正式支持的覆写机制,并让原订阅保持可更新。

用最小配置与同版本核心验证,确认后再恢复完整内容

大型配置一次可能包含订阅节点、规则集、DNS、TUN 和脚本。最有效的修复方法不是一次删掉所有“可疑段”,而是保存原件后制作工作副本,围绕第一条错误做最小改动。若仍难定位,可在离线副本中二分注释非必要段落;每次都保持 YAML 层级完整,并记录删除了哪些区域。

可以使用与你的客户端相同版本、同一平台的 Mihomo 二进制执行配置测试;常见命令形式如下,实际路径需要替换为自己的脱敏测试副本:

mihomo -t -f /path/to/config.yaml

不要从未知网站下载另一个“验证器核心”。通用在线 YAML 检查器可能上传整份配置并泄露凭据,也只能验证通用语法。更安全的做法是本地测试;若必须共享,只共享复现错误所需的最小片段。

  1. 对原文件和最近可用版本各留一份只读副本。
  2. 先修第一条语法或类型错误,再运行同版本检查。
  3. 逐条处理剩余错误,不因错误数量多就一次重写。
  4. 测试通过后由客户端加载副本,先保持系统代理和 TUN 关闭。
  5. 检查配置、代理组、DNS 日志,再做一条实际连接。
  6. 确认稳定后才替换生产配置,并保留回退副本。

完成标准:命令行或客户端配置检查无错误、核心能持续运行、预期策略组存在、日志没有回到旧文件,并且一次实际请求命中可解释的规则。只有“文件保存成功”还不够。

若修复后仍报未知字段,把核心版本、第一条完整错误、字段所在的脱敏父级和参考的官方文档链接一起提交给维护者。不要附带订阅 Token、服务器密码或整份生产配置。

官方来源与版本边界

版本号、文件名和界面文字可能随项目更新而变化,操作前请以链接中的项目文档为准。