Skip to content

fix: preserve JSON-safe checkpoint serialization for msgpack async saves#182

Merged
bsbodden merged 2 commits intomainfrom
fix/msgpack-serialization-fix
Mar 28, 2026
Merged

fix: preserve JSON-safe checkpoint serialization for msgpack async saves#182
bsbodden merged 2 commits intomainfrom
fix/msgpack-serialization-fix

Conversation

@vishal-bala
Copy link
Copy Markdown
Member

@vishal-bala vishal-bala commented Mar 27, 2026

fixes #181

Summary

Issue #181 reported that AsyncRedisSaver could fail during checkpoint writes with:

TypeError: Object of type HumanMessage is not JSON serializable

The failure showed up when checkpoint state contained LangChain message objects and RedisJSON received those live Python objects instead of a serialized representation.

Root Cause

The failing path was the checkpoint serializer fallback for non-JSON-safe payloads.

When a checkpoint contains binary data anywhere in its state, JsonPlusRedisSerializer.dumps_typed(...) can fall back from JSON to MessagePack. In the Redis saver, _dump_checkpoint() then deserialized that MessagePack payload back into Python objects before writing it to Redis. That worked for raw bytes, which were partially normalized, but it also rehydrated LangChain messages like HumanMessage and AIMessage as live objects.

At that point, the checkpoint document was no longer fully RedisJSON-safe, and AsyncRedisSaver eventually passed those message objects down to client.json().set(...), which raised the serialization error from the original issue.

In short: the bug was not in normal JSON checkpoint writes. It was in the msgpack fallback path, where deserialization reintroduced Python objects that RedisJSON cannot store directly.

Fix

This change keeps the existing JSON path unchanged and narrows the fix to the msgpack branch in _dump_checkpoint().

For msgpack checkpoint payloads, the saver now:

  1. Deserializes the typed payload back into Python values.
  2. Replaces nested raw bytes with Redis-safe marker dictionaries.
  3. Runs the normalized checkpoint back through the serializer so LangChain messages are converted back into their JSON-safe constructor form.
  4. Loads the resulting JSON document for Redis storage.

This preserves the expected RedisJSON representation while still allowing checkpoint payloads that require MessagePack during intermediate serialization.

Tests

Added regression coverage for both the serializer boundary and the async Redis path:

  • A focused unit regression that reproduces the msgpack fallback shape with LangChain messages plus binary state and asserts the dumped checkpoint remains JSON-safe.
  • An async Redis-backed regression that verifies AsyncRedisSaver can store and reload that mixed checkpoint shape without raising the original HumanMessage is not JSON serializable error.

These tests connect directly to issue #181 by exercising the exact failure mode: message objects becoming live Python instances again after MessagePack fallback.

@vishal-bala vishal-bala self-assigned this Mar 27, 2026
@vishal-bala vishal-bala added the bug Something isn't working label Mar 27, 2026
@vishal-bala vishal-bala marked this pull request as ready for review March 27, 2026 12:09
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 649a36aa1e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@vishal-bala vishal-bala requested a review from bsbodden March 27, 2026 15:42
Copy link
Copy Markdown
Contributor

@bsbodden bsbodden left a comment

Choose a reason for hiding this comment

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

LGTM!

@bsbodden bsbodden merged commit e2a9448 into main Mar 28, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AsyncRedisSaver Serialization TypeError

2 participants