SchemaSafe Blog
JSON Schema validation without an LLM
Schema validation belongs in deterministic tooling: the same input produces the same pass or fail, every time. An LLM can explain why a document failed or draft a schema from an example, but the pass/fail bit should come from a real validator. When the boundary between explanation and decision stays clear, the contract remains something you can test and trust.
Determinism is the product
When I design a validator I focus on the walk through the instance tree. Each step produces an error object that carries a JSON Pointer path, the failing keyword, and a short message. Because the walk follows the same algorithm for the same data, the error list is identical on every run. That repeatability lets teams treat validation as a gate that never flips its verdict without a real change in the schema or the input.
JSON Pointer paths map directly to form fields or API response keys. A front‑end can take the pointer /items/2/name and highlight the third item’s name field without any extra translation layer. This mapping is deterministic, so the same pointer always points to the same location in the same document shape.
In CI I run the validator against the test suite and against any generated fixtures. If the validator returns an empty error array the build proceeds; any non‑empty array fails the build and the error payload is published as an artifact. Because the output is machine readable, downstream steps can count errors, group them by pointer, or post a comment on the pull request that shows the exact failing paths.
Where an LLM earns its place
I use a language model only for tasks that do not require a true/false verdict. The model never decides pass or fail; it only produces suggestions that a human or a tool can review.
Given a handful of sample JSON documents I prompt the model to emit a Draft 2020‑12 schema that captures the observed types, required fields, and basic patterns. The output is a starting point; I run it through the validator to see if it accepts the examples and then I tighten or loosen constraints manually.
When the validator returns an array of errors I feed each error object to the model with a request to rewrite the message in plain language. The model can turn “failed keyword “required” at pointer /user/profile/age” into “The user profile is missing an age field, which your service expects to be an integer.” This helps API consumers who are not familiar with JSON Schema keywords.
By showing the model a list of frequent validation errors I can ask it to propose additional constraints, such as a minimum string length or a pattern for an identifier. I review each suggestion, add the ones that make sense, and run the validator again to confirm the new schema still passes the known good samples.
For safety I keep the model behind an internal API and I log every prompt and response. The log lets me audit whether the model ever attempted to influence the decision boundary, which it cannot because I never pass its output to the validator as a rule.
Run validation in CI and at the edge
The same schema should guard the code repository and the live service. I run the validator in two places:
Running validation only at a gateway leaves the internal service exposed to drift; if the gateway schema is updated but a downstream service forgets to pull the change, bad data can slip through. By embedding the validator in the service I guarantee that the contract is enforced wherever the code lives.
When validation fails I return HTTP 400 with a JSON body that contains an array of error objects. Each error object includes:
Clients can parse this array and show inline form errors, which speeds up integration work compared to a generic “validation failed” string.
Because the validator is stateless and deterministic, I can use the same binary or library in both places without worrying about hidden state that could cause divergence.
Draft generation versus governance
SchemaSafe separates concerns clearly:
This split keeps the contract auditable. I can point to a specific schema file in the repository, note the validator version, and show that the same input always yields the same result. The model’s contribution lives in separate logs or in comments attached to the pull request; it never becomes part of the immutable decision record.
When I need to update the schema I follow these steps:
Throughout this flow the validator is the gatekeeper; the model never gets the final say.
What this is NOT
This approach is not a promise that every invalid document will be caught before it reaches a user. If a developer deliberately bypasses the middleware or if a bug in the validator lets an error slip through, bad data can still appear. I do not claim 100 % coverage; I only claim that the check is deterministic and repeatable.
It is not a replacement for thoughtful API design. A schema can describe shape and basic constraints, but it cannot express business rules that depend on external state or complex cross‑field logic. Those rules still need unit tests or runtime guards.
It is not a guarantee that the LLM will always produce helpful explanations. The model may occasionally miss nuance or produce a misleading rewrite. I treat its output as a suggestion that a human can accept, edit, or discard.
Finally, it is not a silver bullet for security. While validating input reduces many injection risks, I still follow the guidance from OWASP on prompt injection ( https://genai.owasp.org/llmrisk/llm01-prompt-injection ) and on credential hygiene in CI/CD ( https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-06-Insufficient-Credential-Hygiene ) to keep the surrounding pipeline safe.
References
https://genai.owasp.org/llmrisk/llm01-prompt-injection
https://owasp.org/www-project-top-10-ci-cd-security-risks/CICD-SEC-06-Insufficient-Credential-Hygiene
https://ajv.js.org/json-schema.html
https://cheatsheetseries.owasp.org/cheatsheets/LLM_Prompt_Injection_Prevention_Cheat_Sheet.html
FAQ
Validation must be reproducible: same input, same result. A model's judgment can vary between runs, which is unacceptable for a contract that gates a deployment. The validator decides; the model explains.
Drafting a schema from an example, rewriting a validation error for a human, or suggesting why a field was omitted. Those are assists on top of a real validator, not a substitute for one.
No. Validate inbound requests in the service you control so error messages include domain context. Run the same schema in CI against fixtures, and treat schema changes as potential breaking changes.
Draft 2020-12 is the stable version supported by modern validators such as Ajv v8 and aligned with OpenAPI 3.1. Pin the $schema URL in every file so behavior does not drift.
Try the product: SchemaSafe
2026-09-16 · primary sources only · no fabricated traffic metrics
Related articles
Common JSON Schema authoring mistakes
Explore the full AI micro-SaaS topic matrix →
Why not let an LLM decide if JSON is valid?
Validation must be reproducible: same input, same result. A model's judgment can vary between runs, which is unacceptable for a contract that gates a deployment. The validator decides; the model explains.
Where is an LLM genuinely useful for schemas?
Drafting a schema from an example, rewriting a validation error for a human, or suggesting why a field was omitted. Those are assists on top of a real validator, not a substitute for one.
Should validation run only at the gateway?
No. Validate inbound requests in the service you control so error messages include domain context. Run the same schema in CI against fixtures, and treat schema changes as potential breaking changes.
What is the current JSON Schema draft to pin?
Draft 2020-12 is the stable version supported by modern validators such as Ajv v8 and aligned with OpenAPI 3.1. Pin the $schema URL in every file so behavior does not drift.
Decision-support only. Not a substitute for formal schema review or certified audits.