解决一次 “Failed to load model because protobuf parsing failed”:从现象到根因与修复
这篇文章记录我在 Windows 上使用 Magika 时遇到的报错 “Failed to load model because protobuf parsing failed” 的完整调查、根因分析与解决方案。问题具有代表性:涉及 Python 包里安装的 Rust CLI、编译期模型内嵌、以及 Windows 下符号链接的坑点,值得留档备忘。
问题背景
- 环境与操作:
- 在 D:\1_AI\magika\1\magika-cli-v1.0.0\python 路径下执行 uv sync。
- 同目录下生成 .venv,其 Scripts 目录内出现 magika.exe。
- 运行命令(powershell里python目录下运行):
uv run magika ..\tests_data\basic\python\code.py
报错:
Error: Failed to load model because protobuf parsing failed.
- 疑问:
- 这个 magika.exe 是从哪里来的?
- 它运行时到底加载哪里的模型?
关键事实
- 这个 magika.exe 来自项目的 Rust CLI,通过 maturin 作为 Python 包安装到虚拟环境的可执行入口。
- Rust CLI 不读取 site-packages\magika\models\... 的模型;而是将模型“编译进二进制”:
- 代码位置:rust/lib/src/builder.rs
- 关键调用:
include_bytes!("model.onnx")
- 这个 model.onnx 文件在仓库中并不是实际的模型,而是一个符号链接(symlink),指向 ../../gen/model/model.onnx:
- rust/lib/src/model.onnx 实际内容(如果 symlink 被破坏)会变成只有一行:
../../gen/model/model.onnx
- 若符号链接在检出时没有被保留(Windows 上常见),编译器会把这一行“文本字节”内嵌到二进制,导致运行时 ONNX 解析失败,报 “protobuf parsing failed”。
为什么会发生在 Windows 上(symlink 差异)
- 在 Linux/macOS:
- 符号链接是原生文件类型,git 默认以 symlink 记录(mode 120000),检出后就是一个真正的 symlink;include_bytes! 能读取到目标模型的真实字节。
- 在 Windows:
- 虽然 NTFS 支持 symlink,但默认创建/检出 symlink 需要管理员权限或开启“开发者模式”,git 还需要 core.symlinks=true。
- 如果条件不满足,git/解压工具常把 symlink 退化为“普通文本文件”,内容就是目标路径这一行。
- 于是编译阶段 include_bytes! 内嵌的是“路径文本”,运行时被 ONNX Runtime 当模型解析,自然失败(protobuf 无法解析)。
如何快速判断你中招了
- 打开文件 rust/lib/src/model.onnx,如果看到的不是二进制,而是只包含一行:
../../gen/model/model.onnx
就是 symlink 被破坏的证据。
- 或者你用的是官方打包的 Rust CLI,构建时嵌入的模型来自 rust/gen/model/model.onnx;若这个链路被破坏或指向无效,运行也会失败。
两条修复路线
- 路线 A:继续用 Rust CLI(magika.exe),修好内嵌模型
1) 直接把“真实的 ONNX 模型文件”覆盖到 rust/lib/src/model.onnx:
- 源:assets\models\standard_v3_3\model.onnx
- 目标:rust\lib\src\model.onnx(覆盖掉那行文本)
2) 在 python 目录执行重建安装:
uv sync
3) 再运行:
uv run magika ..\tests_data\basic\python\code.py
- 路线 B:改用 Python 版 CLI,显式指定模型目录(跳过 Rust 的内嵌模型)
uv run python -m magika.cli.magika_client --model-dir "D:\1_AI\magika\1\magika-cli-v1.0.0\assets\models\standard_v3_3" ..\tests_data\basic\python\code.py
- Python CLI 会从你指定目录读取 model.onnx 与 config.min.json,与 symlink 是否正确无关。
常见误区与排查清单
- 误区:
- 以为 magika.exe 会读取 site-packages\magika\models\...。实际上 Rust CLI 使用的是“编译期内嵌模型”,你手动往 site-packages 放模型不会生效。
- 以为报错来自 onnxruntime 版本不兼容。事实上,绝大多数类似报错来自“嵌入的不是模型二进制,而是一段文本”。
- 排查清单:
- 打开 rust/lib/src/model.onnx 看内容是否是一行相对路径。
- 确认 uv run magika -V 是 Rust CLI(版本输出会包含模型名)。
- 若想使用 Python CLI,走模块入口并用 --model-dir。
- 如需校验模型文件是否正常,可在 Windows 上查看大小或计算哈希:
Get-FileHash "D:\...\model.onnx" -Algorithm SHA256
- Python 依赖中 onnxruntime 版本(来自 python/pyproject.toml):
- Python ≤ 3.9:onnxruntime>=1.17.0,<1.20.0
- Python > 3.9:onnxruntime>=1.17.0
- 一般无需额外调整。
小结
- 报错 “Failed to load model because protobuf parsing failed” 的根因是:Windows 上 symlink 未被正确保留,导致 rust/lib/src/model.onnx 退化为“文本指针”,include_bytes! 把文本嵌入到可执行文件,运行时 ONNX Runtime 解析失败。
- 解决要么覆盖为真实模型并重建,要么直接用 Python CLI 指定模型目录,要么恢复 symlink 能力后重新检出构建。
- 一句话经验:看到这种 protobuf 解析失败,先怀疑“模型字节是否真的是 ONNX”,尤其在 Windows 上涉及 symlink 的场景。