快速结论:这个断言通常出现在 Windows 上同一个进程反复动态加载、卸载 ggml 动态库(如 ggml.dll、ggml-base-whisper.dll)的场景,Go 侧或调用方在重新加载时触发 GGML_ASSERT(prev != ggml_uncaught_exception) on dynamic library load。优先排查“卸载后再加载”的调用方式和终止处理器的安装逻辑,而不是模型或上下文清理。
适用环境:Issue 中确认的信息为:Windows 11 x64;在 whisper.net 1.9.1 / 1.9.2-preview1 的运行时包中复现(whisper.cpp 子模块约 4979e04f 时期);断言位于 ggml/src/ggml.cpp:22;亦可由直接调用 kernel32 的 LoadLibraryW/FreeLibrary 稳定复现。Python、CUDA、PyTorch、显卡型号等信息 Issue 未提供,不做推断。
最快修复方案:暂无确认的一步修复方案。Issue 中唯一被使用者实际采用且报告有效的规避方式是设置 GGML_NO_BACKTRACE 环境变量来关闭该终止处理器机制;另外 Issue 提出了一行“容忍并重装”的代码修改(见解决步骤),但属于提议方案,未在主线合入验证,可优先尝试。
注意事项:GGML_NO_BACKTRACE 只是绕过断言,会同时失去该机制提供的未捕获异常回溯信息;提议的代码修改改变了断言语义,需自行在目标版本上回归,不能视为官方修复。该 Issue 因长期无活动被标记 stale 后关闭,评论者指出在当前 master 上仍可复现,并另开 #30875 跟进,因此按原 Issue 状态无法确认已修复。
问题场景
用户在 Windows 上从一个可执行程序(或托管运行时)动态加载 ggml 动态库,使用完毕后在卸载前清理了 context、samples 和 model,但再次加载同一个或同源的 ggml 动态库时,进程在库静态初始化阶段触发断言并退出。Issue 中提到,该加载/卸载模式已是至少一个下游消费方(whisper.net 的进程内后端切换)的正式功能,因此不只是实验性用法。
报错原文
GGML_ASSERT(prev != ggml_uncaught_exception) on dynamic library load
// 定位:ggml/src/ggml.cpp:22
// 复现时的进程退出码:0xC0000409
原因分析
可能原因(Issue 中有较明确的分析):ggml 在静态初始化时调用 std::set_terminate 安装自定义终止处理器,并用 prev 记录调用前的处理器。std::set_terminate 属于进程级 CRT 状态,宿主 DLL 卸载时不会被还原。在 Windows 上重新映射同一二进制时,系统会复用刚释放的虚拟地址块,而各份同源副本把该函数放在相同的早期 .text RVA,于是卸载后全新一次首次加载看到的 prev 在数值上等于 ggml_uncaught_exception,与“重复安装”无法区分,断言因此误杀正常场景。同时,同一路径的加载本身有引用计数,真正的重复安装在该路径下并不会发生。
此外,Issue 正文提到调用方虽然清理了 context、samples、model 再卸载,仍会偶发(并非每次)出现该异常,说明问题更可能出在库的加载/卸载生命周期,而不是推理对象的释放顺序。
环境排查
- 确认操作系统与架构:Issue 中为 Windows 11 x64,其他 Windows 版本未验证。
- 确认 ggml 动态库来源与文件名,例如 ggml.dll、ggml-base-whisper.dll,以及是否来自 whisper.net 1.9.1 / 1.9.2-preview1 等打包运行时。
- 确认对应源码版本,Issue 中追踪到 whisper.cpp 子模块约 4979e04f 时期,并称在同步至当年 master 的源码上断言仍在 ggml/src/ggml.cpp:22。
- 检查代码或运行环境中是否设置了 GGML_NO_BACKTRACE,该变量会整体关闭本机制。
- 梳理加载/卸载路径:是否存在 FreeLibrary 直到引用计数归零后再 LoadLibraryW 的循环,或加载同源但不同版本的副本。
- Python、CUDA、PyTorch、显卡等版本 Issue 未涉及,遇到其他形态的类似断言时需另行确认。
解决步骤
- 先稳定复现:在测试程序中用 LoadLibraryW 加载目标 ggml 库,循环 FreeLibrary 至返回 false,再 LoadLibraryW 重新加载,确认是否每次都在 ggml.cpp:22 触发断言。
- 若复现成立,可优先尝试规避:在启动加载方进程前设置环境变量 GGML_NO_BACKTRACE,关闭该终止处理器安装逻辑,再验证加载/卸载是否恢复正常。这是 Issue 中下游消费方当前采用的规避方式。
- 若需要保留异常回溯能力,可优先尝试 Issue 中提出的代码修改:当检测到 prev == ggml_uncaught_exception 时,不直接断言,而是重新调用 std::set_terminate(ggml_uncaught_exception),将 previous_terminate_handler 置空并正常返回,即“容忍并重装”。
- 修改后在本地重新编译 ggml 库,并同步到使用它的运行时包中,避免仍加载旧 DLL。
- 若要追踪官方进展,关注该 Issue 的后续跟进 #30875,而不是以本 Issue 的关闭状态判断已修复。
验证方法
用与复现时相同的 LoadLibraryW/FreeLibrary 循环脚本反复执行多次,确认不再出现 GGML_ASSERT 断言且进程退出码不再是 0xC0000409;随后在实际业务路径中重复执行一次完整的“加载库 → 初始化 → 推理 → 清理 → 卸载 → 重新加载”流程,确认后端切换正常、无进程级退出。若使用了 GGML_NO_BACKTRACE,还需确认该环境只作用于测试或可控的加载方进程。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


![RuntimeError: [...] /project/cpp/grammar_parser.cc:564: EBNF parser error at line 2, column 1: Rule "root" is defined multiple times](https://www.chat-gpts.plus/wp-content/uploads/2026/10/43920-c079389d-768x403.jpg)