跪拜 Guibai
← All articles
Frontend · Backend · Artificial Intelligence

TypeBox Catches LLM Tool-Call Arguments That Are Valid JSON but Wrong Types

By 东方小月 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Model-generated JSON that parses successfully can still carry string-wrapped numbers, missing required fields, or unexpected prototypes, and those errors land inside file I/O or shell commands where they are harder to diagnose. A strict, schema-driven validation step that shares a single source of truth with TypeScript types stops malformed arguments before any side effects run.

Summary

LLM tool calls often produce syntactically correct JSON that still breaks downstream code — a string `"20"` where a number is expected, for instance. This implementation adds a second validation boundary that sits between the event-stream state machine and actual tool execution. It uses TypeBox to define each tool’s parameter schema once, then derives both compile-time TypeScript types and runtime validators from the same definition.

Two entry points handle every path arguments can arrive by: `parseToolArguments` for raw JSON strings and `validateToolArguments` for already-parsed objects. Both paths enforce that the root is a plain object, deep-clone the data to prevent post-validation mutation, and run strict TypeBox checks that reject implicit type coercion. Failures produce a structured `ToolArgumentsValidationError` carrying the tool name and a list of field-level issues, without leaking the original arguments.

The approach keeps unsafe `any` out of the pipeline. `JSON.parse` results are immediately narrowed to `unknown`, and the `Check` method acts as a type guard so that successful branches return `Static<TParameters>` with no cast needed. Invalid schemas — a developer mistake — are left to throw their own errors rather than being swallowed by the validation layer.

Takeaways
TypeBox schemas serve double duty: they produce JSON Schema for runtime validation and derive exact TypeScript types via `Static<T>`.
`ToolDefinition<TParameters>` preserves the concrete schema type so validated results carry accurate field types without casts.
`validateToolArguments` deep-clones input with `structuredClone` before checking, preventing callers from mutating validated objects afterward.
Both `parseToolArguments` (JSON string) and `validateToolArguments` (unknown object) funnel through the same plain-object check, clone step, and schema validator.
`ToolArgumentsValidationError` reports the tool name and a list of field-level issues but deliberately omits the raw arguments to avoid leaking sensitive data.
TypeBox’s `Check` method is a type guard; the success branch narrows to `Static<TParameters>` so no `as` assertion is needed.
Invalid schemas — a developer configuration error — are not caught by the validation functions, keeping schema bugs distinct from model-argument bugs.
Conclusions

Most LLM tool-calling pipelines treat valid JSON as the finish line, but a parsed object can still carry string-wrapped numbers, missing keys, or prototype pollution that crashes downstream code.

Using `structuredClone` before validation isn’t just defensive copying — it also rejects objects containing functions or non-cloneable values that a model should never produce for tool arguments.

Tightening `JSON.parse`’s `any` return to `unknown` before validation is a small, high-leverage move that forces every code path through the schema check instead of trusting the parser.

The deliberate choice not to catch `Compile` errors means a broken schema fails fast during development rather than masquerading as a model argument problem in production logs.

Concepts & terms
TypeBox
A TypeScript library that builds JSON Schema objects at runtime while simultaneously deriving static TypeScript types from them, so one schema definition drives both compile-time type-checking and runtime validation.
JSON Pointer
A string syntax (RFC 6901) for referencing specific fields inside a JSON document using paths like `/options/labels/0`. TypeBox uses it to report which field failed validation.
structuredClone
A browser and Node.js built-in that creates a deep copy of a value, rejecting objects that contain functions, DOM nodes, or other non-cloneable items. Used here to isolate validated arguments from external mutation.
Type Guard
A TypeScript pattern where a function’s return type narrows a variable’s type inside a conditional block. TypeBox’s `Check` method acts as a type guard, so a passing check tells the compiler the value matches the schema.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗