POST https://api.openai.com/v1/chat/completions "HTTP/1.1 400 Bad Request

Could you help me with this error:?

code:

GENERAL_AND_SALES_AGENT_LLM = model_gpt_4_1_mini.bind_tools(
    GENERAL_AND_SALES_AGENT_TOOLS,
    response_format=ModelResponse,
    strict=True,
)

class OrderItem(BaseModel):
    name: str
    quantity: int = Field(..., gt=0)
    price: Optional[float] = None
    note: Optional[str] = None

@tool
async def capture_customer_record(
    state: Annotated[AgentState, InjectedState()],
    record_type: RecordType,
    contact_name: str,
    contact_phone: str | None = None,
    contact_email: str | None = None,
    order_items: List[OrderItem] | None = None,
    notes: str | None = None,
) -> str:

ERROR:

{"error": "Error code: 400 - {'error': {'message': \"Invalid schema for function 'capture_customer_record': In context=('properties', 'order_items', 'anyOf', '0', 'items'), 'required' is required to be supplied and to be an array including every key in properties. Missing 'price'.\", 'type': 'invalid_request_error', 'param': 'tools[2].function.parameters', 'code': 'invalid_function_parameters'}}"

hi @meharaz733

This is OpenAI’s strict-mode schema validator rejecting your nested OrderItem, not a problem with your tool logic.

In strict mode OpenAI requires that every object lists all of its keys in required - there are no truly optional fields; an “optional” field must instead be required and nullable. The error path ('properties','order_items','anyOf','0','items') points exactly at OrderItem inside List[OrderItem] | None. Because price/note have defaults, Pydantic emits required: ["name","quantity"], so OpenAI complains “Missing ‘price’”.

Why LangChain doesn’t fix it for you: in convert_to_openai_function, strict=True forces required = all keys only at the top level (your tool args), and _recursive_set_additional_properties_false recurses into nested objects to set additionalProperties:false but never rewrites their required. So your nested model keeps Pydantic’s partial required and gets rejected.

Fix (keep strict): make nested fields required but nullable. In Pydantic v2, Optional[X] without a default is required + nullable:

class OrderItem(BaseModel):
    name: str
    quantity: int = Field(..., gt=0)
    price: Optional[float]   # note: no "= None"
    note: Optional[str]

This now emits required: ["name","quantity","price","note"] with price as anyOf:[number, null] - exactly what strict mode wants. The model is forced to return the keys but may set them to null; your tool treats None as “not provided” as before. (Verified locally on Pydantic 2.13.4.)

Alternative: if you don’t need guaranteed schema adherence, just drop strict=True - standard function calling accepts optional fields - and keep validating tool inputs in Python.