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.