Skip to content

feat(checkpoint-postgres)!: align version format and inline primitive storage with Python#1968

Open
WilliamPeralta wants to merge 3 commits intolangchain-ai:mainfrom
WilliamPeralta:feat/checkpoint-postgres-version-format
Open

feat(checkpoint-postgres)!: align version format and inline primitive storage with Python#1968
WilliamPeralta wants to merge 3 commits intolangchain-ai:mainfrom
WilliamPeralta:feat/checkpoint-postgres-version-format

Conversation

@WilliamPeralta
Copy link

@WilliamPeralta WilliamPeralta commented Feb 17, 2026

Summary

BREAKING: Align checkpoint version format and channel_values storage with the Python implementation.

Breaking changes:

  1. Version format: getNextVersion now produces zero-padded string versions (e.g. "00000000000000000000000000000001.0482910384729105") instead of integer versions (1, 2, 3).

    Migration impact: Existing checkpoints with integer versions will still be readable. New checkpoints will use string versions. Active threads may need to be completed before upgrading, as getNextVersion needs to parse the current version format.

  2. Inline primitives: Primitive channel values (string, number, boolean, null) are now stored inline in the checkpoint JSONB column instead of in checkpoint_blobs. Requires feat(checkpoint-postgres): add task_path support and inline primitive merging #1967 (read-side merge) to be deployed first, so existing readers can handle both formats.

Why:

These changes enable cross-compatibility between Python and JS checkpoint implementations sharing the same database, which is required for hybrid Python/JS LangGraph deployments.

Upgrade path:

  1. Deploy feat(checkpoint-postgres): add task_path support and inline primitive merging #1967 (non-breaking, adds read-side support) to all readers
  2. Then deploy this PR to writers
  3. Old integer-versioned checkpoints remain readable

Test plan

  • Verify getNextVersion produces correct zero-padded string format
  • Verify primitive values are stored inline in checkpoint JSONB
  • Verify complex values are still stored in checkpoint_blobs
  • Verify read-side correctly merges inline + blob values
  • Verify existing integer-versioned checkpoints still readable

@changeset-bot
Copy link

changeset-bot bot commented Feb 17, 2026

🦋 Changeset detected

Latest commit: 7a85bb7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@langchain/langgraph-checkpoint-postgres Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

WilliamPeralta added a commit to WilliamPeralta/langgraph-checkpoint-postgres that referenced this pull request Feb 17, 2026
…s with Python parity fixes

Fixes branching bug (#1812), adds task_path support, inline primitives,
and globally unique version IDs.

Upstream PRs: langchain-ai/langgraphjs#1967, langchain-ai/langgraphjs#1968
@WilliamPeralta
Copy link
Author

This PR directly addresses #1812 — the branching bug caused by deterministic integer versions in getNextVersion().

Root cause: When branching from the same checkpoint, getNextVersion() produces the same next integer version for both branches (e.g., both get version 3). Since checkpoint_blobs uses ON CONFLICT ... DO NOTHING, the second branch's blob data is silently discarded, corrupting state. MemorySaver is unaffected because it doesn't use version-keyed blob storage.

Fix: getNextVersion() now produces globally unique string versions ("00000000000000000000000000000003.7482910384729105") — zero-padded counter + random hash — ensuring no collisions across branches.

In the meantime, we've published a temporary drop-in replacement package with all fixes from both #1967 and this PR:

Usage is a one-line import change:

// Before:
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
// After:
import { PostgresSaver } from "@logicpanel/langgraph-checkpoint-postgres";

We'll deprecate the temporary package once these PRs are merged.

Copy link
Member

Choose a reason for hiding this comment

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

Can we have some unit tests here to ensure we don't regress in the future?

@WilliamPeralta WilliamPeralta force-pushed the feat/checkpoint-postgres-version-format branch from 863dddd to d71f9ae Compare February 24, 2026 09:16
@WilliamPeralta
Copy link
Author

WilliamPeralta commented Feb 24, 2026

Christian Bromann (@christian-bromann) Christian BromannAdded unit tests in src/tests/postgres-saver.test.ts (21 tests) covering:

getNextVersion (8 tests)

  • Produces correct format: 32-char zero-padded counter + dot + 16-char random hash
  • Starts at counter 1 when current is undefined
  • Increments counter correctly from string versions
  • Backward compat: handles legacy integer versions (e.g. 3"0...04.xxx")
  • Produces unique versions across calls (random hash uniqueness)
  • Monotonically increasing counters
  • String sorting works correctly (critical for Postgres ORDER BY)
  • Format matches Python's f"{next_v:032}.{next_h:016}"

_dumpCheckpoint (4 tests)

  • Inlines primitive channel_values (string, number, boolean, null) in checkpoint JSONB
  • Excludes complex values (arrays, objects) from inline storage
  • Handles empty channel_values
  • Preserves all other checkpoint fields

_dumpBlobs (5 tests)

  • Skips primitive values (they go inline in checkpoint)
  • Includes complex values in blob output
  • Correctly separates primitives vs complex in mixed scenarios
  • Returns empty array when no versions
  • Handles channels with version but missing from values (empty type)

_loadCheckpoint (4 tests)

  • Merges inline primitives from checkpoint JSONB with blob values
  • Blob values override inline primitives on key collision
  • Works with empty blob values (inline-only)
  • Works with no inline channel_values (backward compat with pre-inline checkpoints)

All passing: pnpm test ✓, pnpm lint ✓, pnpm format:check

@WilliamPeralta
Copy link
Author

so?

… storage with Python

BREAKING: Align checkpoint version format and channel_values storage
with the Python implementation.

Breaking changes:

1. Version format: getNextVersion now produces zero-padded string
   versions (e.g. "00000000000000000000000000000001.0482910384729105")
   instead of integer versions (1, 2, 3).

   Migration impact: Existing checkpoints with integer versions will
   still be readable. New checkpoints will use string versions.

2. Inline primitives: Primitive channel values (string, number,
   boolean, null) are now stored inline in the checkpoint JSONB column
   instead of in checkpoint_blobs. Requires PR langchain-ai#1967 (read-side merge)
   to be deployed first, so existing readers can handle both formats.

These changes enable cross-compatibility between Python and JS
checkpoint implementations sharing the same database, required for
hybrid Python/JS LangGraph deployments.
…ne primitive storage

Add unit tests covering the changes introduced in this PR:
- getNextVersion: format validation, counter increment, backward compat with integer versions, uniqueness, string sorting, Python format parity
- _dumpCheckpoint: inline primitive extraction (string, number, boolean, null) vs complex value exclusion
- _dumpBlobs: primitive skipping, complex value serialization, mixed value handling
- _loadCheckpoint: inline + blob merge, blob-wins-on-collision, backward compat with missing inline values
@WilliamPeralta WilliamPeralta force-pushed the feat/checkpoint-postgres-version-format branch from d71f9ae to 7a85bb7 Compare March 2, 2026 16:20
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.

2 participants