Skip to content

fix(checkpoint): revive lc=2 JSON blobs for safe types without allowlist#7582

Merged
Sydney Runkle (sydney-runkle) merged 1 commit into
mainfrom
fix/message-coercion-failure-on-resume
Apr 27, 2026
Merged

fix(checkpoint): revive lc=2 JSON blobs for safe types without allowlist#7582
Sydney Runkle (sydney-runkle) merged 1 commit into
mainfrom
fix/message-coercion-failure-on-resume

Conversation

@sydney-runkle

Copy link
Copy Markdown
Collaborator

Summary

Fixes #7498MESSAGE_COERCION_FAILURE when resuming threads checkpointed before v1.0.1.

Root cause: PR #6269 (v1.0.1) added an _allowed_json_modules security gate to JsonPlusSerializer._reviver. The gate defaults to None, so old "json"-format checkpoint blobs containing lc=2 constructor dicts (the pre-msgpack serialization format for pydantic objects like HumanMessage) are now returned as raw dicts instead of being reconstructed. Those raw dicts reach add_messages → convert_to_messages, which sees type="constructor" and raises MESSAGE_COERCION_FAILURE. Fresh first-turn messages are unaffected because current dumps_typed only writes "msgpack" blobs.

Fix: _reviver now reconstructs lc=2 blobs whose target class is already in SAFE_MSGPACK_TYPES — the same curated allowlist already used by the msgpack deserialization path (includes all standard LangChain message types). Unknown classes are still blocked, preserving the security intent of #6269.

Changes

  • libs/checkpoint/langgraph/checkpoint/serde/jsonplus.py — add _is_safe_json_type() helper; update _reviver and _check_allowed_json_modules to allow safe types without an explicit allowlist
  • libs/checkpoint/tests/test_jsonplus.py — two new regression tests: safe-type lc=2 blobs revive correctly; unknown-type lc=2 blobs stay blocked

Test plan

  • test_lc2_json_safe_type_revives_without_allowlistHumanMessage/AIMessage lc=2 JSON blobs round-trip to proper BaseMessage objects with no allowlist configured
  • test_lc2_json_unknown_type_stays_blocked_without_allowlistpprint.pprint lc=2 blob still returns raw dict (not reconstructed)
  • test_deserde_invalid_module — existing behaviour unchanged
  • Full test_jsonplus.py suite: 93/93 passing

…without allowlist

Old checkpoints written before the msgpack migration (pre-v1.0.1) stored
LangChain messages and other safe types as lc=2 JSON constructor dicts.
PR #6269 added an _allowed_json_modules security gate that defaults to None,
causing _reviver to skip reconstruction and return raw dicts.  Those raw dicts
then reach add_messages → convert_to_messages → MESSAGE_COERCION_FAILURE.

Fix: let _reviver reconstruct lc=2 blobs whose class is already in
SAFE_MSGPACK_TYPES — the same curated safe-type list used by the msgpack path.
This restores backwards-compat for pre-v1.0.1 threads without reopening the
broader security gate for arbitrary classes.

Fixes #7498

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sydney-runkle Sydney Runkle (sydney-runkle) changed the title fix(checkpoint/serde): revive lc=2 JSON blobs for safe types without allowlist fix(checkpoint): revive lc=2 JSON blobs for safe types without allowlist Apr 22, 2026
@sydney-runkle Sydney Runkle (sydney-runkle) merged commit 85cd64e into main Apr 27, 2026
68 of 69 checks passed
@sydney-runkle Sydney Runkle (sydney-runkle) deleted the fix/message-coercion-failure-on-resume branch April 27, 2026 13:08
Sydney Runkle (sydney-runkle) added a commit that referenced this pull request Apr 27, 2026
## Summary

Bumps `langgraph-checkpoint` `4.0.2` → `4.0.3` and updates all
downstream `uv.lock` files.

## Changes since 4.0.2

- fix(checkpoint): revive lc=2 JSON blobs for safe types without
allowlist (#7582)
- chore: dedup warnings (#7257)
- chore(deps): bump langsmith from 0.6.4 to 0.7.31 (#7525)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants