Skip to content

Python: fix circular reference error in KernelArguments.dumps() during OTel diagnostics#13642

Open
MaxwellCalkin wants to merge 1 commit intomicrosoft:mainfrom
MaxwellCalkin:fix/kernel-arguments-circular-reference
Open

Python: fix circular reference error in KernelArguments.dumps() during OTel diagnostics#13642
MaxwellCalkin wants to merge 1 commit intomicrosoft:mainfrom
MaxwellCalkin:fix/kernel-arguments-circular-reference

Conversation

@MaxwellCalkin
Copy link

Motivation and Context

Fixes #13393

When running Process Framework steps with SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true, every function invocation fails with Circular reference detected. This happens because:

  1. KernelFunction.invoke() calls arguments.dumps() to serialize function arguments for OpenTelemetry span attributes
  2. Process step functions receive a KernelProcessStepContext argument (injected by find_input_channels() in step_utils.py)
  3. KernelProcessStepContext contains a step_message_channel field, which is the LocalStep instance itself
  4. LocalStep extends KernelBaseModel and contains Kernel, KernelFunction objects, output edges, and other complex runtime objects that form circular reference chains
  5. When dumps() calls model_dump() on the context, then passes the result to json.dumps(), the circular references cause ValueError: Circular reference detected

Description

Makes KernelArguments.dumps() resilient to circular references:

  • Object ID tracking: The custom default serializer now tracks id() of objects it has already visited, returning a safe placeholder string (<circular ref: ClassName>) instead of recursing into circular structures
  • Fallback for model_dump() failures: Wraps model_dump() in a try/except so that if a Pydantic model itself raises during serialization, the type name is used as a placeholder
  • Top-level ValueError catch: If json.dumps() still detects a circular reference (e.g., from nested dicts produced by model_dump()), falls back to per-key serialization where each un-serializable value is replaced with its type name

This is a defensive fix at the serialization boundary — it ensures OTel diagnostics never crash function invocations, regardless of argument complexity.

Contribution Checklist


Disclosure: This PR was authored by Claude Opus 4.6 (an AI). I'm applying for a role at Microsoft — see maxwellcalkin.com for background.

…g OTel diagnostics

When SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE
is enabled, KernelFunction.invoke() calls arguments.dumps() to serialize
function arguments for OpenTelemetry spans. If the arguments contain
objects with circular references (e.g., KernelProcessStepContext, whose
step_message_channel holds back-references to the process graph), this
causes json.dumps to raise 'Circular reference detected'.

The fix adds defensive handling in KernelArguments.dumps():
- Track seen object IDs to detect circular references in the custom
  default serializer
- Catch ValueError from json.dumps and fall back to per-key
  serialization, replacing un-serializable values with their type name

Fixes microsoft#13393
@MaxwellCalkin MaxwellCalkin requested a review from a team as a code owner March 8, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Bug: Python: Circular Reference Error when running Process samples with SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true

1 participant