Troubleshooting

Which JSON Schema Keywords Gemini Structured Output Accepts, Tested

Eren Şimşir· Founder & Chief Technical Editor•
Close-up of vibrant HTML code displayed on a computer screen, showcasing web development and programming.
Photo by Pixabay via Pexels

The two schema fields accept different keywords

In my tests, the OpenAPI-style responseSchema field of the Gemini API returned HTTP 400 for additionalProperties, $schema, $id, $ref and a type array, while the newer responseJsonSchema field accepted all of them. Two keywords were accepted without constraining the output reliably (allOf on both models I tried, oneOf on one), so a valid JSON reply can still break your schema.

  • If you get Unknown name "additionalProperties" ... Cannot find field, you are sending a JSON Schema keyword to responseSchema. Remove it or switch to responseJsonSchema.
  • nullable: true works in responseSchema; "type": ["string", "null"] works only in responseJsonSchema.
  • Accepted does not mean enforced: allOf and, on one model, oneOf produced output that failed my schema check.
  • Validate the reply in your own code every time, whatever the API accepted.

How I tested

On 9 October 2026 I sent small generateContent requests to the Gemini API with a free-tier key: gemini-3.6-flash for the responseSchema field, gemini-3.7-flash for the responseJsonSchema field, and gemini-3.5-flash-lite for the extra experiments at the end. The prompt was the same each time (extract a user id and a status from one sentence), temperature was 0, and every schema was a small variation of one base object. I then checked each reply against the schema I had sent with Python's jsonschema 4.25.0 (version 7 validator). The free tier allows 20 requests per model per day, so every case is a single sample: it shows what the API did once, not how often.

body = {
    "contents": [{"role": "user", "parts": [{"text": "Extract the profile: user id 9912, email admin@example.com, status active."}]}],
    "generationConfig": {
        "temperature": 0,
        "responseMimeType": "application/json",
        "responseSchema": schema,        # or "responseJsonSchema": schema
    },
}
# POST to https://generativelanguage.googleapis.com/v1beta/models/<model>:generateContent
# then: jsonschema.Draft7Validator(schema).validate(json.loads(reply_text))

Results with responseSchema (gemini-3.6-flash)

Schema featureAPI responseReply matched the schema?
Base object with enum and requiredHTTP 200Yes
additionalProperties: falseHTTP 400: Unknown name "additionalProperties" ... Cannot find field.n/a
$schema and $idHTTP 400: Unknown name "$schema" ... Cannot find field.n/a
$ref to $defsHTTP 400: Unknown name "$ref" ...n/a
"type": ["string", "null"]HTTP 400: Proto field is not repeatingn/a
nullable: trueHTTP 200Yes (returned null)
title, descriptionHTTP 200Yes
anyOf (string or integer)HTTP 200Yes
oneOf (string or integer)HTTP 200No: returned an object where the schema allows a string or an integer
allOf at the rootHTTP 200No: returned different key names (user_id, not userId)
pattern, format: date-time, minimum/maximum, defaultHTTP 200Yes (one sample each)

Results with responseJsonSchema (gemini-3.7-flash)

I ran the same cases through the responseJsonSchema field. Everything that failed with HTTP 400 above was accepted here.

Schema featureAPI responseReply matched the schema?
additionalProperties: falseHTTP 200Yes
$schema and $idHTTP 200Yes
$ref to $defsHTTP 200Yes (a one-property schema)
"type": ["string", "null"] and nullable: trueHTTP 200Yes
anyOf, oneOfHTTP 200Yes
allOf at the rootHTTP 200No: required key userId missing, other key names returned
pattern, format, minimum/maximum, defaultHTTP 200Yes (one sample each)

The reply to the oneOf case matched the schema on this model, although it did not on the other one. That difference between models is the practical lesson: a keyword the API accepts can behave differently from one model to the next.

Which schema keywords each Gemini field acceptsTable of nine schema features. With responseSchema, four are rejected with HTTP 400, oneOf and allOf are accepted but the reply broke the schema. With responseJsonSchema, the four rejected ones are accepted and only allOf at the root still produced a reply that broke the schema.Which schema keywords each Gemini field acceptsresponseSchema(3.6-flash)responseJsonSchema(3.7-flash)additionalProperties: false400 rejectedaccepted$schema and $id400 rejectedaccepted$ref to $defs400 rejectedaccepted"type": ["string","null"]400 rejectedacceptednullable: trueacceptedacceptedanyOfacceptedacceptedoneOfreply broke itacceptedallOf at the rootreply broke itreply broke itpattern, format,min/max, defaultacceptedaccepted
From the two result tables above, one sample per case. "Reply broke it" means the API returned HTTP 200 but the reply did not match the schema that was sent.

Three things I expected to fail and that did not

  • A system instruction that asks for an explanation. I told the model to explain its reasoning step by step before answering while a schema was set. The reply was still valid JSON that matched the schema (on gemini-3.7-flash and on gemini-3.5-flash-lite).
  • Extra fields. I asked for "any other helpful fields" with and without additionalProperties: false (on gemini-3.5-flash-lite, and without the keyword also on gemini-3.7-flash). Every reply contained only the schema's two properties. One sample each, so this does not prove the keyword is unnecessary.
  • Deep nesting. Objects nested 5 levels and 12 levels deep were both accepted and filled correctly (gemini-3.5-flash-lite). Google's documentation only says that very large or deeply nested schemas may be rejected, without a number.

What to do with this

  1. Pick the field on purpose. If your schema comes from Pydantic or Zod and contains additionalProperties, $ref or type arrays, use responseJsonSchema. If you stay on responseSchema, strip those keywords or the request fails with HTTP 400 before any generation.
  2. Treat HTTP 400 and a wrong reply as different problems. A 400 names the offending key in the message, so read it. A 200 with the wrong shape needs a validator in your code.
  3. Avoid allOf and be careful with oneOf. Flatten the schema into one object with optional fields, or use anyOf, which behaved in my tests.
  4. Validate and retry. Parse the reply, validate it with jsonschema or your Pydantic model, and retry once with the validation error appended to the prompt when it fails.

What this test did not cover

Each case ran once at temperature 0 on a very small schema, so it says nothing about failure rates or about large schemas. I did not test Gemini 3.1 Pro (the free tier has a quota of zero for it), the Node.js SDK, Vertex AI, streaming, or the response_format parameter that Google's current structured-output page documents; I tested the older generateContent fields. Model names change quickly, so rerun a handful of these cases on the model you use.

Sources and Further Reading

Frequently Asked Questions

Why does the Gemini API say Unknown name "additionalProperties" at response_schema?

Because the OpenAPI-style responseSchema field in generateContent does not know that JSON Schema keyword. In my test, additionalProperties, $schema, $id and $ref all returned HTTP 400 with "Cannot find field". The responseJsonSchema field accepted them. Remove the keyword or switch fields.

Does Gemini structured output guarantee that the reply matches my schema?

No. Valid JSON came back every time in my tests, but with oneOf (on gemini-3.6-flash) and allOf (on both models) the reply did not match the schema. Always validate the parsed reply in your own code and handle the failure.

How do I allow null values in a Gemini response schema?

With responseSchema, set nullable: true next to the base type; a type array such as ["string", "null"] was rejected with HTTP 400. With responseJsonSchema, both forms were accepted in my test.

Can I use $ref and $defs in a Gemini schema?

Not in responseSchema: a $ref returned HTTP 400. In responseJsonSchema a simple $ref to a $defs entry was accepted and the reply matched. I only tested a one-property schema, so test your real, larger schema before relying on references.

Does a system prompt that asks the model to explain break structured output?

Not in my test. With a schema set and a system instruction to explain the reasoning first, two models (gemini-3.7-flash and gemini-3.5-flash-lite) still returned valid JSON that matched the schema. One sample per model, so keep your prompts focused on the data anyway.