Skip to content

Commit b461c95

Browse files
wdunn001claude
andcommitted
Codec: harden _fetch_manifest against non-dict JSON payloads
Address the remaining open bot comment on this PR (the four others are covered by 2758102 — async dispatch + cached registry + hardened varint + struct unpack). If a manifest URL returns a JSON list, scalar, or null, the existing `if required not in parsed` check raises TypeError with an unhelpful message. Reject non-dict shapes up front with a clear ValueError that names the URL and actual type. Also document why `_fetch_manifest` stays synchronous: it's only called from `ToolRegistry.from_env`, which the engine wraps in `asyncio.to_thread`, so the blocking urlopen never runs on the request event loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: William Dunn <wdunn001@gmail.com>
1 parent 77467e1 commit b461c95

1 file changed

Lines changed: 13 additions & 1 deletion

File tree

vllm/entrypoints/codec_dispatcher.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,23 @@ def from_env(cls, active_tokenizer_hash: str) -> "ToolRegistry":
175175

176176
def _fetch_manifest(url: str) -> dict:
177177
"""Fetch a tool manifest JSON from the URL. Stdlib-only to keep the
178-
engine startup path free of heavy HTTP deps."""
178+
engine startup path free of heavy HTTP deps.
179+
180+
Synchronous on purpose — only called from `ToolRegistry.from_env`,
181+
which the engine wraps in `asyncio.to_thread` so this never runs on
182+
the request event loop. See `dispatch_call_async` for the runtime-
183+
request equivalent.
184+
"""
179185
import urllib.request
180186
with urllib.request.urlopen(url, timeout=30) as resp:
181187
body = resp.read()
182188
parsed = json.loads(body)
189+
if not isinstance(parsed, dict):
190+
# Defend against the URL returning a JSON list/scalar — the
191+
# field-presence loop below would TypeError on a non-dict.
192+
raise ValueError(
193+
f"manifest at {url!r} must be a JSON object, got {type(parsed).__name__}"
194+
)
183195
# Minimum required fields per @codecai/tool-kit's manifest.json shape.
184196
for required in ("name", "endpoint", "tokenizerHash"):
185197
if required not in parsed:

0 commit comments

Comments
 (0)