What does DeepSeek's response_format json_object guarantee, and what does it not?
answer
- Valid JSON, not correct JSON
- Syntax guarantee versus shape guarantee
- Prompt must contain the word json
- Truncation produces unparseable output
- Validate after parsing, always
basics
~20 sDeepSeek's JSON output mode constrains the model to emit syntactically valid JSON, nothing more. It does not validate against a schema you supply, so field names, types and required keys remain your responsibility to prompt for and then verify.
solid answer
~50 sSetting `response_format={"type": "json_object"}` on a DeepSeek chat call makes the model produce parseable JSON instead of prose or fenced markdown. That is a syntax guarantee, not a shape guarantee: as of mid-2026 DeepSeek offers only the coarse object mode, with no schema-constrained equivalent, so nothing enforces that `price` is a number or that `items` is present. Two practical requirements come with it. The prompt must actually ask for JSON — the word `json` needs to appear in your system or user message — and you should include a small example of the structure you want, because the example is what pins down the field names. You must also allow enough `max_tokens`: if generation is cut off mid-object the output is truncated and no longer parses. Treat the result as untrusted input — parse defensively and validate against your own model before use.
code
python · 30 linesimport json, os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)
system = (
"Extract the ticket fields and reply in json with keys "
'title (string) and priority (string). Example: '
'{"title": "Disk full", "priority": "high"}'
)
resp = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": "The build box ran out of space overnight."},
],
response_format={"type": "json_object"},
max_tokens=256,
)
choice = resp.choices[0]
if choice.finish_reason == "length":
raise RuntimeError("output truncated before the object closed")
data = json.loads(choice.message.content)
print(data["title"], data["priority"])go deeper
Know that the flag makes the model return parseable JSON and that you still call a JSON parser on the result. Say plainly that the prompt must also ask for JSON.
Explain the difference between syntactic validity and schema conformance, why truncation breaks parsing, and how an example in the prompt does the work a schema would otherwise do.
Show the production path: check the finish reason, parse defensively, validate against a typed model, and retry with the validation error fed back — plus logging the raw text when validation fails.
Own the portability point: identical field names with weaker guarantees are the real risk in compatible APIs, so a shared client layer must surface capability differences rather than let them pass as equivalent.
## The two different guarantees It helps to separate two things that people conflate under "structured output". **Syntactic validity** means the response is well-formed JSON — balanced braces, quoted keys, no trailing commentary, no markdown fence around it. This is what DeepSeek's JSON output mode gives you. It is genuinely useful, because the single most common failure of naive prompting is a model that wraps its answer in prose or a ```json fence and breaks your parser. **Schema conformance** means the response matches a contract you declared: these keys exist, this one is an integer, that one is an enum of three values. This is a strictly stronger guarantee and DeepSeek's JSON mode does not provide it. There is no schema parameter to hand it and no strict mode to switch on, so schema conformance stays a prompting-and-validation problem on your side. ## How to enable it On a chat completion, pass `response_format={"type": "json_object"}`. Because the API is OpenAI-shaped, the field name and the object mode are identical to what you already write against OpenAI — which is exactly why the missing schema mode is easy to overlook when porting. Code that passes a JSON Schema in `response_format` will not quietly degrade to object mode; it will be rejected or ignored, and either way you lose the guarantee you thought you had. ## The prompt requirements Two requirements are documented and both bite in practice. First, the word `json` must appear somewhere in the prompt — system or user message. This is a guard against a common misuse where a caller flips the flag but still asks for a paragraph, leaving the model in an impossible position. Second, describe the structure you want and show a small example. Since no schema is enforced, that example is the only thing anchoring your key names. "Return json with keys title (string) and tags (array of strings), for example {\"title\": \"...\", \"tags\": [\"a\"]}" is dramatically more reliable than "return json". ## Truncation is the classic bug JSON mode does not extend the token budget. If the object the model wants to emit is longer than the remaining `max_tokens`, generation stops mid-structure and you receive a prefix — `{"items": [{"name": "wid` — which is not valid JSON. The tell is a `finish_reason` of `length` rather than `stop`, so check it before you even attempt to parse. Budget generously for list-shaped outputs, and consider asking for fewer items per call rather than raising the ceiling indefinitely. The defensive habit is: check the finish reason, parse inside a try/except, and on failure either retry with a nudge or fall back to a repair path. Never let a parse exception propagate as a 500 to your own callers. ## Validate after parsing Because the shape is unenforced, parsing is only step one. Run the parsed object through a real validator — a Pydantic model, a JSON Schema validator, a typed decoder — and treat validation failure as a retryable model error, not as a bug in your code. Two extra behaviours are worth handling: extra keys the model invented (decide whether to ignore or reject them) and stringly-typed numbers, where you asked for `count: 3` and got `"3"`. Coercion at the boundary is usually kinder than a hard failure. It is also worth logging the raw text on validation failure. Without the raw output you cannot tell whether the model drifted, the prompt example was ambiguous, or the response was truncated. ## When to use something else If you genuinely need a hard shape guarantee, JSON mode plus validation plus a bounded retry loop is the realistic pattern on this provider. For strongly constrained extraction, keep the schema small and flat — deeply nested objects with optional branches are where unconstrained generation drifts most. An alternative for well-defined operations is to express the contract as a tool definition and read the arguments from the tool call, since the arguments are themselves a JSON payload described by a schema in the request; you still validate, but you have given the model a much more explicit target. ## The migration takeaway The field is spelled the same, the mode name is spelled the same, and the code compiles. What changed silently is the strength of the guarantee. That asymmetry — identical syntax, weaker semantics — is the defining hazard of an OpenAI-compatible provider, and JSON output is its clearest example.
- Your parse fails intermittently on long outputs. What do you check first?The finish reason. A value of `length` means generation hit the token ceiling and the object was cut off mid-structure, which is a budget problem, not a model-quality problem. Raise `max_tokens`, or reduce how much you ask for per call — for example by paginating a list instead of requesting all items at once — and only then investigate prompt phrasing.
- How would you get a stronger shape guarantee on this provider?Combine three things: a small, flat structure described with a concrete example in the prompt; validation of the parsed object against a real schema or typed model on your side; and a bounded retry that feeds the validation error back as a correction turn. Expressing the contract as a tool definition and reading the call arguments is an alternative target that is more explicit than free-form JSON.
- Does passing a JSON Schema in response_format degrade gracefully to object mode?No — do not rely on that. Code ported from a provider with schema-constrained output must be rewritten rather than assumed to fall back, because the failure mode is either an error or a request that no longer carries the guarantee you believe it does. Make the difference explicit in your client layer instead of hoping the server smooths it over.
saying these in an interview costs you the question
- Assuming JSON mode validates your field names and types
- Enabling the flag without asking for JSON in the prompt
- Never checking finish_reason before parsing
- Believing a JSON Schema in response_format is honoured
- Skipping validation because the output parsed