Skip to content

fix: serialize NaN/Inf floats nested in pydantic models as JSON-safe strings - #1817

Open
uuzzrm wants to merge 1 commit into
langfuse:mainfrom
uuzzrm:fix/serialize-nan-in-pydantic-models
Open

fix: serialize NaN/Inf floats nested in pydantic models as JSON-safe strings#1817
uuzzrm wants to merge 1 commit into
langfuse:mainfrom
uuzzrm:fix/serialize-nan-in-pydantic-models

Conversation

@uuzzrm

@uuzzrm uuzzrm commented Aug 15, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes #16048

EventSerializer's BaseModel branch returned obj.model_dump() directly, so non-finite floats nested inside a pydantic model bypassed the NaN/Inf sanitizer that the dict/list branches already apply. Python's JSON C encoder then emitted bare NaN/Infinity tokens, which strict JSON parsers reject. On the tracing path, any traced pydantic object carrying a NaN/Inf float (common with ML/scoring payloads) produced an event body that failed ingestion.

The fix routes the dumped dict back through default(), so the same sanitization applies at every nesting depth:

from pydantic import BaseModel
from langfuse._utils.serializer import EventSerializer

class Scores(BaseModel):
    confidence: float

# before: {"confidence": NaN}  (bare NaN, invalid JSON)
# after:  {"confidence": "NaN"}
EventSerializer().encode(Scores(confidence=float("nan")))

Type of change

  • Bug fix

Verification

pytest tests/unit/test_serializer.py -q -k "not path"     # 29 passed
ruff check langfuse/_utils/serializer.py tests/unit/test_serializer.py  # all checks passed
ruff format --check langfuse/_utils/serializer.py tests/unit/test_serializer.py

The new regression test test_pydantic_model_with_non_finite_float_serializes_to_valid_json fails before the fix (strict parse raises ValueError: NaN) and passes after. test_path fails on this machine only (POSIX-vs-Windows path separators) and passes in CI's Linux runner; the same pre-existing mypy import-not-found note for langchain_core is present on the unmodified file.

Checklist

  • I self-reviewed the diff using code_review.md.
  • I added or updated tests for behavior changes.
  • I updated docs, examples, or .env.template if needed.
  • I did not hand-edit generated files; if generated files changed, I used the upstream regeneration path.
  • I did not commit secrets or credentials.

Greptile Summary

Routes Pydantic model_dump() output back through EventSerializer so nested non-finite floats become JSON-safe strings.

  • Sanitizes nested NaN and infinity values consistently with existing dict/list handling.
  • Adds regression coverage using strict JSON parsing for Pydantic payloads.

Confidence Score: 5/5

The PR appears safe to merge, with the intended Pydantic serialization behavior covered by a focused strict-JSON regression test.

The changed branch reuses the established recursive normalization path, ensuring non-finite floats nested in Pydantic models no longer produce invalid bare JSON constants, and no concrete blocking or non-blocking defect remains.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Pydantic BaseModel] --> B[model_dump]
  B --> C[EventSerializer recursive normalization]
  C --> D[NaN and Infinity converted to strings]
  D --> E[Valid JSON ingestion payload]
Loading

Reviews (1): Last reviewed commit: "fix: serialize NaN/Inf floats nested in ..." | Re-trigger Greptile

Context used:

…strings

The BaseModel branch of EventSerializer returned model_dump() directly,
so non-finite floats nested inside a pydantic model bypassed the NaN/Inf
sanitizer that the dict/list branches apply. The JSON C encoder then
emitted bare NaN/Infinity tokens, which strict JSON parsers reject -- a
traced pydantic object carrying a NaN/Inf float produced an event body
that failed ingestion. Route the dumped dict back through default() so
the same sanitization applies at every nesting depth.

Fixes #16048.

Signed-off-by: Ruiming Zhao <uuzzrm@gmail.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

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.

1 participant