RuntimeError: […] /project/cpp/grammar_parser.cc:564: EBNF parser error at line 2, column 1: Rule “root” is defined multiple times

该报错通常出现在 vLLM V1 引擎启用了结构化输出(structured outputs)并提交 EBNF grammar 的场景:第一个 grammar 请求成功锁定 xgrammar 后端后,后续请求即使本身应被自动回退到其他后端,仍会交给 xgrammar 编译;若 grammar 非法,

快速结论:该报错通常出现在 vLLM V1 引擎启用了结构化输出(structured outputs)并提交 EBNF grammar 的场景:第一个 grammar 请求成功锁定 xgrammar 后端后,后续请求即使本身应被自动回退到其他后端,仍会交给 xgrammar 编译;若 grammar 非法,编译异常会穿透调度循环,导致 EngineCore 崩溃、引擎死亡。优先排查:失败请求中的 grammar 是否合法、是否是同一引擎内“先合法后非法”的请求序列。

适用环境:Issue 中确认:vLLM v0.20.0、v0.20.1、v0.21.0;xgrammar 0.2.0;V1 Engine(默认);structured outputs backend 使用 CLI 默认值 auto;Linux;PyTorch 2.4+(任意构建);Python 版本在原报告中未完整给出。

最快修复方案:暂无确认的一步修复方案。Issue 讨论中验证了相关 PR #47312(使 grammar 编译失败时 EngineCore 保持存活,并返回 400)及 #44401(修改 scheduler 与 HTTP 错误传播)的方向;此外 #49625 针对后续请求解析到不同后端的情况增加了准入一致性校验。需按实际使用的 vLLM 版本确认这些改动是否已合入。

注意事项:“重启引擎”只能恢复服务,不能消除根因,下一次“先合法、后非法”的请求序列仍可能复现。Issue 中同时指出,在修复后仍存在单后端锁定与 per-request backend=auto 解析不一致的残余问题(例如同一引擎内先来 xgrammar 兼容请求、再来需要 guidance 的 schema,第二个请求被错误路由),该部分由 #49625 处理,是否已合入需以实际版本为准。

问题场景

在 vLLM V1 引擎上启动 OpenAI 兼容服务(例如 vllm serve,structured outputs backend 保持默认 auto),先发送一个 xgrammar 可接受的 grammar 请求(如 root ::= "a" | "b"),成功返回后,再发送一个 xgrammar 在编译期拒绝的非法 EBNF(如重复定义 root)。第二个请求返回 HTTP 500,引擎进程崩溃并重启。

报错原文

RuntimeError: [...] /project/cpp/grammar_parser.cc:564: EBNF parser error at line 2, column 1: Rule "root" is defined multiple times

(EngineCore pid=...) ERROR [core.py] EngineCore encountered a fatal error.
  ...
  File ".../vllm/v1/structured_output/__init__.py", line 183, in _create_grammar
    return self.backend.compile_grammar(request_type, grammar_spec)

HTTP 500: {"error":{"message":"EngineCore encountered an issue. See stack trace (above) for the root cause.",...}}

原因分析

V1 的 StructuredOutputManager 进程内只支持一个结构化输出后端:它在第一个到达 scheduler 的 grammar 请求上按该请求的 _backend 初始化 XgrammarBackend / GuidanceBackend,并缓存到引擎生命周期结束。per-request 的 auto 回退只在提交该请求时判断,因此第一个请求若被 xgrammar 接受,后端即被锁定为 xgrammar;后续所有 grammar 请求都会走 XgrammarBackend.compile_grammar。

而该函数对 self.compiler.compile_grammar(...) 没有异常处理,非法 grammar 在编译期抛出的异常会从后台编译 future 传播到 _try_promote_blocked_waiting_request → EngineCore.run_busy_loop,最终触发 EngineDeadError,所有 TP worker 退出。

可能原因也包括:请求序列中前后 grammar 实际解析出的后端不一致(如 JSON schema 中带 multipleOf 的请求应解析到 guidance),却被路由到已锁定的 xgrammar 编译器,从而返回 HTTP 500。

环境排查

  • 确认 vLLM 版本:Issue 中确认存在问题的版本为 v0.20.0、v0.20.1、v0.21.0。
  • 确认引擎为 V1(默认),且 structured outputs backend 为 CLI 默认的 auto。
  • 确认 xgrammar 版本为 0.2.0。
  • 确认 PyTorch 版本(Issue 为 2.4+)、Linux 环境及 Python 版本。
  • 记录发往服务的 grammar 请求顺序,确认是否出现“先合法、后非法”或“前后请求应解析到不同后端”的情况。
  • 查看服务端日志中 EngineCore encountered a fatal error 的完整堆栈,定位到 backend_xgrammar.py 的 compile_grammar。

解决步骤

  1. 先定位触发崩溃的第二个请求,检查其 grammar 是否为合法 EBNF;Issue 中的示例是同一 grammar 内重复定义 root,xgrammar 在编译期拒绝。
  2. 核对当前 vLLM 版本是否已包含 PR #47312 的处理逻辑:grammar 编译失败时不再杀死 EngineCore,而是返回 400。若未包含,可优先尝试升级到包含该修复的版本。
  3. 核对是否已包含 #44401 对 scheduler 与 HTTP 错误传播的修改;该 PR 的作用是让引擎保持存活并让非法 grammar 返回 400,而不是让异常穿透到 busy loop。
  4. 若服务在同一引擎内处理需要不同后端(xgrammar 与 guidance)的请求,检查是否已包含 #49625 的准入一致性校验;未包含时,后续解析到不同后端的请求仍可能被路由到错误的编译器并返回 HTTP 500。
  5. 在无法升级或补丁未合入的环境下,可优先尝试在业务侧做前置校验:在提交前拒绝非法 grammar,并避免在同一个引擎实例内混用会被不同后端解析的 schema。
  6. 若引擎已崩溃,重启服务仅恢复可用性;重启后仍需对照上述步骤确认修复是否生效,避免重复触发。

验证方法

按 Issue 中的复现顺序重新执行:先提交合法 grammar 请求 root ::= "a" | "b",应返回 HTTP 200;再提交非法 EBNF root ::= "a"\nroot ::= "b",在修复后的行为下应返回 400,且服务端日志不再出现 EngineCore encountered a fatal error,EngineCore 进程保持存活、无需重启。对于混合后端场景,可验证第二个应解析到 guidance 的请求不再被错误路由并返回 500。

参考来源

vllm-project/vllm #43920

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 29543

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注