Error database

BadRequestError 400: Invalid schema for function (tool calling)

The JSON Schema you declared for a tool breaks the provider's validation rules — strict mode has extra requirements beyond plain JSON Schema. Generate schemas from Pydantic models instead of writing them by hand.

The message you saw
BadRequestError 400: Invalid schema for function (tool calling)

By Updated

The error

Output
openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid schema for function 'get_weather': In context=(), 'required' is required to be supplied and to be an array including every key in properties. Missing 'unit'.", 'type': 'invalid_request_error', 'param': 'tools[0].function.parameters', 'code': 'invalid_function_parameters'}}

The detail after the function name varies — missing items on an array, an unsupported keyword, a bad type value — but the shape is constant.

What it means

Tool calling lets a model invoke your functions, and each function is described by a JSON Schema — a formal declaration of its parameters. The provider validates your schema before accepting the request, and yours failed. The message quotes the exact rule broken and where; read it closely, because it names the field to fix.

Why it happens

Hand-written schemas drift from the rules. The strictest rules come with OpenAI's structured outputs (strict: true), which demand more than generic JSON Schema:

  • every key in properties must also appear in required
  • every object must set "additionalProperties": false
  • only a subset of JSON Schema keywords is supported

Other classics: an array type without items, "type": "int" instead of "integer", and optional-parameter patterns that strict mode does not allow (optionality is expressed with a union type including "null", not by omission from required).

How to fix it

1. Fix exactly what the message names. For the error above, unit exists in properties but not in required:

json
{
  "type": "object",
  "properties": {
    "city": {"type": "string"},
    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
  },
  "required": ["city", "unit"],
  "additionalProperties": false
}

2. Stop writing schemas by hand — derive them from Pydantic.

python
from pydantic import BaseModel

class GetWeather(BaseModel):
    city: str
    unit: str

print(GetWeather.model_json_schema())

A generated schema is always syntactically valid, and the SDKs increasingly accept Pydantic models directly for tools and structured outputs — use that path when your SDK offers it.

3. Check nesting depth and every nested object. The rules apply recursively: a nested object three levels down missing additionalProperties: false fails the whole schema. The In context=(...) part of the message is the path to the offending level.

4. For Anthropic, the same discipline applies to input_schema. Each tool's input_schema must be a valid JSON Schema with top-level "type": "object". Invalid schemas return a 400 naming the tool.

5. Re-validate after every edit. Schema errors compound; fix one, resend, read the next message. The validator stops at the first violation, so several may be queued behind the one you see.

How to prevent it

One source of truth per tool: define the parameters as a Pydantic model, generate the schema, and use the same model to parse the arguments the LLM sends back. Handwritten JSON in three places is how schemas rot.