From 83cfb578f061af052c2fca097d295564ede821af Mon Sep 17 00:00:00 2001 From: Madan Kumar Date: Tue, 22 Sep 2026 23:08:57 +0530 Subject: [PATCH] Python: Fix nested required properties marked as optional in OpenAPI plugins When building the function schema from an OpenAPI spec, _get_payload_properties recursed into nested object schemas but kept passing the top level required list down at every level. A nested property's is_required was checked against the root object's required array instead of the nested object's own one, so a required nested field like address.street ended up marked optional. The generated tool then advertises it as optional, the model can omit it, and the request fails schema validation on the API side. The top level caller already seeds each object with its own required list, so do the same in the recursion. Adds a unit test for the nested case. --- .../openapi_plugin/openapi_parser.py | 4 ++- .../openapi_plugin/test_openapi_parser.py | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py b/python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py index 34bea1bff2dd..1c58332b58a7 100644 --- a/python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py +++ b/python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py @@ -118,7 +118,9 @@ def _get_payload_properties(self, operation_id, schema, required_properties, lev name=property_name, type=property_schema.get("type", None), is_required=property_name in required_properties, - properties=self._get_payload_properties(operation_id, property_schema, required_properties, level + 1), + properties=self._get_payload_properties( + operation_id, property_schema, property_schema.get("required", set()), level + 1 + ), description=property_schema.get("description", None), schema=property_schema, default_value=default_value, diff --git a/python/tests/unit/connectors/openapi_plugin/test_openapi_parser.py b/python/tests/unit/connectors/openapi_plugin/test_openapi_parser.py index b0c81074e5a8..e2d7cff1920c 100644 --- a/python/tests/unit/connectors/openapi_plugin/test_openapi_parser.py +++ b/python/tests/unit/connectors/openapi_plugin/test_openapi_parser.py @@ -383,3 +383,28 @@ def test_create_functions_propagates_enable_file_ref_resolution(): enable_file_ref_resolution=True, enable_http_ref_resolution=False, ) + + +def test_get_payload_properties_nested_required(): + parser = OpenApiParser() + schema = { + "type": "object", + "required": ["address"], + "properties": { + "address": { + "type": "object", + "required": ["street"], + "properties": { + "street": {"type": "string"}, + "city": {"type": "string"}, + }, + }, + }, + } + properties = parser._get_payload_properties("operation_id", schema, schema.get("required", [])) + address = properties[0] + assert address.is_required + nested = {prop.name: prop.is_required for prop in address.properties} + # street is in the address object's own required list, not the root one + assert nested["street"] + assert not nested["city"]