Skip to content

OpenAI Integration: truncate_messages_by_size fails on Pydantic SDK objects (e.g., ResponseFunctionToolCall) #5350

@mecampbellsoup

Description

@mecampbellsoup

How do you use Sentry?

Sentry Saas (sentry.io)

Version

2.49.x (and likely all versions with the OpenAI integration)

Steps to Reproduce

  1. Enable the OpenAI integration with sentry-sdk[openai]
  2. Use the OpenAI Responses API with tool calls
  3. When building the follow-up API call after tool execution, include SDK objects (like ResponseFunctionToolCall) in the input list - as shown in OpenAI's documentation
from openai.types.responses import ResponseFunctionToolCall

# After a tool call, building input for follow-up request
input_list = [
    {"role": "user", "content": "Please notify the team"},
    tool_call,  # ResponseFunctionToolCall - a Pydantic object
    {"type": "function_call_output", "call_id": tool_call.call_id, "output": "Success"},
]

# This triggers Sentry's instrumentation which calls json.dumps() on input_list
response = client.responses.create(model="gpt-4o", input=input_list)

Expected Result

Sentry's instrumentation should handle Pydantic/SDK objects gracefully, either by:

  • Calling .model_dump() on them (as _normalize_data() already does)
  • Using a fallback serialization strategy

Actual Result

TypeError: Object of type ResponseFunctionToolCall is not JSON serializable

The error originates from sentry_sdk/ai/utils.py:truncate_messages_by_size() which calls json.dumps(messages) directly without normalizing Pydantic objects first.

Root Cause

The SDK already has _normalize_data() in sentry_sdk/ai/utils.py that correctly handles Pydantic objects:

def _normalize_data(data: "Any", unpack: bool = True) -> "Any":
    # convert pydantic data (e.g. OpenAI v1+) to json compatible format
    if hasattr(data, "model_dump"):
        return _normalize_data(data.model_dump(), unpack=unpack)
    ...

However, truncate_messages_by_size() doesn't use it:

def truncate_messages_by_size(messages, ...):
    serialized_json = json.dumps(messages, separators=(",", ":"))  # No normalization!
    ...

Suggested Fix

Call _normalize_data() before json.dumps():

def truncate_messages_by_size(messages, ...):
    normalized = _normalize_data(messages, unpack=False)
    serialized_json = json.dumps(normalized, separators=(",", ":"))
    ...

Or apply the same fix in truncate_and_annotate_messages() before calling truncate_messages_by_size().

Additional Context

This is the same class of bug as #4923 (handling Omit/NotGiven types) - the OpenAI integration assumes inputs are plain Python dicts, but OpenAI's SDK uses Pydantic objects throughout.

OpenAI's own documentation shows passing SDK objects directly in follow-up requests, so this is a common pattern that Sentry's instrumentation should handle.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions