From 4686d7b833c705000656bdf472868c35b3b932b9 Mon Sep 17 00:00:00 2001 From: Nolan Date: Mon, 26 Oct 2026 02:00:47 +0800 Subject: [PATCH] fix(so): validate required so_args keys before building the matcher MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build() already rejects the unsupported serialized and bound_token_ids keys with a clear message, but read the two required keys with a bare subscript. A so_args missing schema raised KeyError('schema') on the scheduling thread, and one missing schema_type raised inside the worker closure, where try_result() swallowed it and reported the request as "xgrammar unknown Error: 'schema_type'" — an API misuse surfacing as an opaque grammar failure. Check both keys up front, next to the existing so_args validation, and read schema_type on the caller's thread like schema so the worker closure no longer touches so_args. --- deepinfer/structure_output/so_matcher_builder.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deepinfer/structure_output/so_matcher_builder.py b/deepinfer/structure_output/so_matcher_builder.py index 030d2dc..ab3b867 100644 --- a/deepinfer/structure_output/so_matcher_builder.py +++ b/deepinfer/structure_output/so_matcher_builder.py @@ -61,6 +61,10 @@ class SOMatcherBuilder: raise ValueError("Offline structured output does not accept serialized grammars; provide schema_type and schema") if "bound_token_ids" in so_args: raise ValueError("bound_token_ids is no longer supported; use bound_token_id with a single non-negative integer") + missing = [key for key in ("schema_type", "schema") if key not in so_args] + if missing: + raise ValueError(f"so_args is missing required key(s) {missing}; provide schema_type and schema") + schema_type = so_args["schema_type"] schema = so_args["schema"] trigger_tokens = so_args.get("trigger_token_ids") @@ -76,7 +80,7 @@ class SOMatcherBuilder: return XGrammarMatcherFuture(task=failed) def _build() -> XGrammarMatcher: - ctx = self._compiler.dispatch_and_compile(so_args["schema_type"], schema) + ctx = self._compiler.dispatch_and_compile(schema_type, schema) matcher = XGrammarMatcher.from_compiled( ctx, self._vocab_size,