microsoft/semantic-kernel · error · Exception

Max level {OpenApiParser.PAYLOAD_PROPERTIES_HIERARCHY_MAX_DE

Error message

Max level {OpenApiParser.PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH} of traversing payload properties of `{operation_id}` operation is exceeded.

What it means

`_get_payload_properties` recurses into nested object schemas to build the payload property tree, guarded by `PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH`. Once `level` exceeds that constant, it raises a bare `Exception` naming the `operation_id`. This prevents unbounded recursion on self-referential / deeply-nested schemas.

Source

Thrown at python/semantic_kernel/connectors/openapi_plugin/openapi_parser.py:107

            result.append(
                RestApiParameter(
                    name=name,
                    type=schema.get("type", "string") if schema else "string",
                    location=location,
                    description=description,
                    is_required=is_required,
                    default_value=default_value,
                    schema=schema if schema else {"type": "string"},
                )
            )
        return result

    def _get_payload_properties(self, operation_id, schema, required_properties, level=0):
        if schema is None:
            return []

        if level > OpenApiParser.PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH:
            raise Exception(
                f"Max level {OpenApiParser.PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH} of "
                f"traversing payload properties of `{operation_id}` operation is exceeded."
            )

        result = []

        for property_name, property_schema in schema.get("properties", {}).items():
            default_value = property_schema.get("default", None)

            property = RestApiPayloadProperty(
                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),
                description=property_schema.get("description", None),
                schema=property_schema,
                default_value=default_value,
            )

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Redesign the schema to use `$ref` for recursion (and ensure the resolver keeps refs) instead of inline nesting.
  2. Flatten or simplify the request body schema for the failing operation.
  3. Reduce the depth of the offending DTO / split it into multiple endpoints.
  4. If you control the parser constant locally, raise `PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH`, but treat that as a workaround — the root cause is schema shape.

Example fix

# before: recursive schema inlined -> infinite depth
Pet:
  type: object
  properties:
    parent: { schema inline of Pet }   # raises 1491

# after: use $ref
Pet:
  type: object
  properties:
    parent:
      $ref: '#/components/schemas/Pet'
Defensive patterns

Strategy: validation

Validate before calling

MAX_DEPTH = 10  # mirror PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH

def schema_depth(schema, seen=None) -> int:
    seen = seen or set()
    if not isinstance(schema, dict):
        return 0
    props = schema.get("properties", {})
    # treat $ref as terminal to avoid false positives
    if schema.get("$ref") or id(schema) in seen:
        return 0
    seen = seen | {id(schema)}
    return 1 + max((schema_depth(p.get("schema", p), seen) for p in props.values()), default=0)

def too_deep(spec, max_depth=MAX_DEPTH) -> list[str]:
    bad = []
    for path, methods in spec.get("paths", {}).items():
        for method, d in methods.items():
            rb = (d.get("requestBody") or {}).get("content", {})
            for mt, meta in rb.items():
                sch = meta.get("schema", {})
                if schema_depth(sch) > max_depth:
                    bad.append(f"{method} {path} {mt}")
    return bad

Type guard

def is_self_referential_schema(schema, seen=None) -> bool:
    seen = seen or set()
    sid = id(schema)
    if sid in seen:
        return True
    seen = seen | {sid}
    for p in (schema or {}).get("properties", {}).values():
        if is_self_referential_schema(p.get("schema", p), seen):
            return True
    return False

Try / catch

try:
    kernel.add_openapi_plugin(plugin_name="x", openapi_parsed_spec=spec)
except Exception as e:  # bare Exception per source
    if "Max level" in str(e) and "payload properties" in str(e):
        # flatten / use $ref, then retry
        raise
    raise

Prevention

When it happens

Trigger: A request-body schema with nesting deeper than `PAYLOAD_PROPERTIES_HIERARCHY_MAX_DEPTH`, including self-referential schemas (`$ref` to an ancestor) that the resolver expanded inline, producing effectively infinite depth.

Common situations: Recursive schemas (tree/graph node types) where `$ref` resolution inlined the cycle; very deeply nested DTOs; an over-large bundled spec; a resolver configuration that expanded `$ref`s instead of keeping them as references.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/55f6399815607de2. Report an issue: GitHub.