Build1 distinct publisher3 min readPublished
Constrained decoding, a repair parser, validation and feedback retries do four different jobs. A dev.to walkthrough shows where each one stops working, and where a 30-second retry budget runs out.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The trap is in the API surface. The same `format` parameter takes either a string or a schema object, and the two do entirely different work. The string `"json"` is steering: the model is pushed toward emitting a valid object, and nothing enforces field names, types or required keys, so `{"result": "..."}` is a legal answer to a request for `severity` and `summary` [2]. The schema object is enforcement [3]. Both calls return without complaint, and the response gives you no way to tell which of the two you got.
Enforcement here is mechanical, which is why it is worth the migration. Ollama zeroes the probability of any token that would violate the schema at every generation step, so a markdown fence is not discouraged, it is unreachable [3]. No prompt wording does that. On Ollama 0.3.0 or newer the Python client passes the schema through with Pydantic's `model_json_schema()` [4], and the dev.to walkthrough reports that for straightforward schemas on a 7B-or-larger model this alone ends the parse failures [5].
The mask has a narrow remit, though. The same piece lists three things that still bite, and only one of them sits inside that remit: small models fumbling nested and optional fields [6]. Of the other two, one is a deployment fact, since an older Ollama or any endpoint offering only loose JSON mode never applies the mask at all [7]. The other is a job no decoder constraint can do. Where the text says the salary is competitive and your schema demands an integer, the model supplies an integer [9]. Two of the three named failure modes are therefore outside what constrained decoding can reach [3].
That second one is the expensive failure, because it survives every later layer. A fabricated integer satisfies the grammar that produced it and satisfies the validator that inspects it, since both only ever asked what type the value was [2]. The feedback retry does not catch it either: the retry is triggered by a validation error, and there is no validation error to feed back [10].
The retry ladder is where the budget breaks. `instructor` gives it to you in configuration, with `max_retries=2` and a `timeout` the source flags as total across retries rather than per call, precisely because local models are slow [11]. At the example's 30 seconds, that is three model calls in 30 seconds, roughly ten apiece [1], on hardware chosen because you own it. Rung two of the ladder is theoretical unless that number goes up.
On the defensive side, `json_repair` earns its place as the `json.loads` replacement, in preference to the regex that strips fences until the night it does not [8][1]. Its default ordering is the safe one: stdlib parse first, repair parser only on failure, so valid input passes through untouched [12]. `skip_json_loads=True` removes that fast path, and the library's own docs warn against pointing it at input you expect to be valid, because the repair parser can reshape valid JSON [13]. Where you cannot vouch for the provenance of the bytes, `strict=True`, which raises instead of repairing, is the setting that tells you the truth [16].
And `instructor` does not replace the mask. It talks to the OpenAI-compatible endpoint, which still depends on the backend honouring the schema [14]. Constrain, defend, validate, retry are four jobs, not four names for one [15]. The retries are what you run when the constraint is not there.
Ranked by verification strength, evidence, and original report placement.
The parsed object should always be validated against the contract, and on failure the model should be retried with the error fed back rather than blind re-rolled; one to three attempts is the right ceiling, after which the pipeline should fail loudly and keep the raw output for debugging.
A local model asked for JSON commonly returns a code fence, prose such as "Here is your result:" and a trailing comma, so json.loads() raises JSONDecodeError; the naive fix is a regex that strips code fences, which works until it does not.
Ollama's format="json" means JSON mode: the model is steered to emit a valid JSON object, but it does NOT enforce field names, types or required keys, so you can still get {"result":"..."} when you wanted {"severity":"high","summary":"..."}.
When format is set to a JSON Schema object, Ollama applies constrained decoding: at every generation step it sets the probability of any token that would violate the schema to zero, so the model physically cannot emit a markdown fence, surrounding prose, or a structurally invalid object.
The official recommended pattern is to pass a real JSON Schema via Pydantic's model_json_schema() to the Python Ollama client, and it works on Ollama 0.3.0 or newer.
A different local server, an older Ollama, or any endpoint that only offers loose JSON mode will not constrain decoding, returning fences and partial objects.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Single-source tutorial with runnable code but no measurement
All assertions trace to one dev.to walkthrough. Its mechanism-level claims are specific and internally consistent, and it supplies working code paths (format=schema, json_repair.loads, a feedback-retry loop) that a reader can execute, which lifts evidence above pure opinion. But there is no second publisher, no vendor documentation in the cluster to verify the Ollama 0.3.0 constrained-decoding behaviour, and no benchmark, failure-rate or latency measurement behind the efficacy claims, so two of the central assertions could only be rated insufficient.
No adoption signal in the cluster
The supplied source is instructional. It contains no release announcement, deployment, benchmark run, usage disclosure, pricing or licensing event — only a version-compatibility assertion ('Ollama 0.3.0 or newer') that describes a feature requirement rather than observed uptake. Because no adoption observation can be grounded in the material, this dimension is left unmeasured rather than inferred from the popularity of the named tools.
Slightly overstated on efficacy, well calibrated on limits
The framing is unusually honest for a tooling walkthrough: it separates steering from enforcement, names three residual failure modes, warns that constrained decoding constrains structure and not truth, and flags the skip_json_loads=True footgun. That self-limiting posture pulls the gap close to aligned. It tips mildly positive because absolute efficacy language ('kills the parse failures', the model 'physically cannot' emit a fence) and a single-line 'Ollama 0.3.0 or newer' guarantee are presented without any measurement, and because the retry configuration is offered as a safety net while its own arithmetic leaves roughly ten seconds per call on hardware the author calls slow.
Individual developer-blog authorship, no disclosed commercial tie
The item is a personal post on dev.to, a platform whose economics reward audience-building and pattern-promotion, and it advocates specific named tools while positioning its own structured_extract() helper as the takeaway. Nothing in the supplied material discloses sponsorship, employment by Ollama, Pydantic, json-repair or instructor, or any commercial interest, and the article recommends free tooling and volunteers limitations against its own thesis. Incentive pressure is therefore modest and attention-driven rather than vendor-driven.
Moderate: verifiable mechanics, unverified efficacy, no second source
Confidence is mid-range. The API-level and library-level facts are precise, self-consistent and demonstrated in code, and the derived arithmetic and layering conclusions follow directly from the stated parameters, so the assessment of what the story says is solid. What limits confidence is structural: one publisher, one article, no corroborating documentation, no measurement behind the two behavioural generalizations, and no adoption dimension at all.
build
45 or 793 tok/s: the same model, and only one of those numbers sizes your box1 distinct publisher
build
Before you buy another GPU, check num_ctx and the rope base1 distinct publisher
build
An empty MCP tool list is a successful response, and the bug is on the server side1 distinct publisher
build
The flash_attn error in llama.cpp is a layout constraint, and it decides your context window1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 26, 2026