Back to the blog

Separating Provider Schemas from Local Validation and Isolating Batch Failures

A multi-provider LLM pipeline separated permissive wire schemas from strict local validation and added bounded repair retries. In one observed batch, failed candidates were queued separately while validated articles completed.

Introduction

I changed a multi-stage LLM pipeline that expected structured output from multiple providers. Before that change, one strict DraftSchema defined both the response shape and application constraints, including string lengths and array bounds. Structured-output failures in the writing and curation stages could take down entire runs. Some responses were rejected before local Zod validation could run, leaving my application unable to inspect and validate the returned content itself.

The evidence points to compatibility problems with those constraints in the failing Anthropic integration. It does not establish the API version, the supported schema subset, or the exact component responsible for every rejection. I treat this as an observed integration limitation, not a general claim about what a provider’s models can enforce.

I split the schema into a simpler provider-facing wire schema and a stricter local Zod schema. The revised curation path also handled JSON extraction and validation locally. It was deployed and reported running without issues when checked. Separately, one batch showed that two failed candidates could be isolated while successful articles completed. These observations show a changed validation boundary and one working case of partial completion—not the elimination of structured-output failures.

The Rule That Shipped

The shipped rule separates what the provider must return from what the application will accept. The wire schema defines a simpler, permissive response shape without the problematic string-length and array-bound constraints. Once a response is available, the application applies stricter validation through its local Zod schema. Passing the provider-facing schema is not enough for application acceptance.

Structured agent-output calls retry up to two times after a validation failure, sending schema-repair instructions back to the model. The retries are bounded: the system does not keep generating until something passes. A persistent mismatch must remain a failure rather than consume an unlimited number of attempts.

I also removed the .max(8) bound from sourceEventIds. That array no longer has a fixed eight-item upper limit. This changed the application’s acceptance rule, not just the provider-facing schema. Leaving the limit in local validation would still reject an otherwise acceptable response with more than eight source-event identifiers.

The curator now extracts JSON and runs Zod validation itself, and curator calls receive a 20,000-token budget. That gives each call more output capacity; it does not establish that responses cannot be truncated or fail validation. These changes address schema handling and failure containment. The available evidence does not establish a separate decomposition of a monolithic prompt.

Worked Example

In the observed batch, two individual article candidates failed structured-output validation partway through processing. Previously, failures of this kind could bring down an entire run. This time, the two failed candidates went into a separate retry queue, while the rest of the batch’s successfully validated articles were preserved and completed.

The batch ended with two distinct states: completed articles that had passed validation, and two unsuccessful candidates awaiting separate handling. Putting a candidate in the retry queue did not make it valid or complete. It kept those failures from invalidating work that had already succeeded.

I can confirm that the batch isolated the failures and preserved the successful articles. I cannot confirm that the two candidates eventually recovered. The evidence does not show whether they later succeeded, failed repeatedly, or remained pending. It also does not establish the total batch size, so there is no basis for calculating a success rate. The result is narrower: two item-level failures did not force the successful remainder to fail.

Challenges and Tradeoffs

The main tradeoff is where to enforce strictness. Sending one strict schema everywhere leaves a single definition to maintain, but that approach failed in this integration because the provider-facing path could not support the constraints as used. Making the same schema permissive everywhere would avoid some compatibility problems while weakening application acceptance. The two-layer design keeps the provider contract simpler without giving up local checks.

Maintaining two schemas introduces a risk of drift. A field can pass the wire schema and fail locally; that is expected when local validation is deliberately stricter. A contradiction between the schemas is a different problem. It could make valid application output difficult or impossible to obtain. That distinction matters when repair attempts keep failing.

Bounded retries trade another chance at recovery for additional calls. Schema-repair instructions give the model an opportunity to correct its output, but two retries do not guarantee a valid response. Batch isolation handles a separate question: what happens to the other items when one candidate remains unsuccessful? The per-call repair limit and the separate retry queue are not evidence of the same recovery policy.

Structural validation also has limits. A response can pass Zod and still contain inaccurate prose or unsuitable content. These changes check structured output and preserved successful items in the observed batch. They do not establish broader content correctness.

Verification Process

I distinguish deployment verification from behavior verification. The revised curation approach—local JSON extraction followed by Zod validation—was deployed and reported running without issues at the check. That is a point-in-time observation, not a measurement of sustained reliability across all providers or future runs.

The batch with two failed candidates verifies a specific behavior: unsuccessful items entered a separate retry queue while validated articles completed and were preserved. It does not verify downstream queue processing. I have not verified successful regeneration, repeated-failure handling, or the eventual disposition of those two items.

Two other limits remain explicit. A database issue recorded the DRAFT step as COMPLETED before the draft call actually ran. The problem was identified, but no fix is confirmed. A completion label alone therefore cannot establish that the underlying operation happened. Separately, removing hard limits on tags and feedback counts was proposed, but the evidence does not confirm that it shipped. Neither issue belongs among the verified schema changes, and neither should be described as a completed repair.

Conclusion

I replaced a single strict provider-facing schema with a permissive wire schema and stricter local Zod validation. I also added bounded schema-repair retries, removed the eight-item sourceEventIds cap, and gave curator calls a 20,000-token budget. The revised curation path was reported running without issues when checked. In one batch, two validation failures were queued separately while successful articles completed.

That supports a changed validation boundary and one successful instance of failure isolation. It does not prove that this class of failure disappeared or that the queued candidates recovered. Their eventual outcomes, the fix for the premature DRAFT completion bug, and the proposed changes to tag and feedback limits remain unverified.