AI Tool Atlas
← Back to the atlas

Developer tools

LangGraph

A real model-free LangGraph/SQLite checkpoint test: approval, restart recovery and the boundary between replay and duplicate writes.

DEPLOYMENTSelf-hosted or managed platform
SOFTWARE TERMSMIT for the repository; verify component and service terms separately
COST MODELOpen-source framework; hosted services and model usage may cost extra
Visit official site ↗

Check model, infrastructure and managed-service pricing separately.

Best for
Developers who need explicit state and review steps
Key limit
A framework requires engineering and does not automatically guarantee safe external actions.
Chinese support
Not verified
Access
Region availability not verified
Audience
International audience · market positioning, not company nationality
Verification
Documentation checked 2026-11-06. Local workflow tested 2026-11-06. Deterministic local nodes and SQLite recovery; no model quality or hosted-service benchmark.
On this page

Start with a state transition you need to control

LangGraph is a low-level orchestration runtime for stateful applications. Its official overview describes graphs of shared state, nodes and edges, with deterministic and model-driven steps. Choose it when you need to inspect that control flow. A single prompt-to-answer task may not need a graph. We originally described the exercise below from documentation. On 6 November 2026 we installed LangGraph and tested a deterministic local version with SQLite checkpoints. The recorded findings follow the original exercise. No model was used or evaluated.

A review gate with a concrete record

Use an invented research-draft task and a fake destination that cannot send messages. The deliverable is a state log and a destination receipt for each test. Define these application fields before assembling the graph:

  • draft_id: demo-17
  • revision: 1
  • draft_text: a short, fixed fixture
  • approved_revision: null
  • action_id: demo-17/revision-1/save
  • receipt_id: null

These are our example fields, not built-in LangGraph guarantees. The intended path is prepare draft → request review → branch on the decision → save to the fake destination → record the receipt. A rejection ends without a write. Approval applies to the displayed revision, so editing the draft after review must invalidate that decision.

Map the documented mechanisms to the exercise

  • Checkpoint state. Compile with a checkpointer appropriate to the trial. The persistence documentation distinguishes thread-scoped checkpoints from cross-thread stores. Its in-memory examples lose their checkpoints when the process exits; do not use an in-memory demonstration as evidence of restart recovery.
  • Pause for a decision. The documented interrupt() mechanism exposes a JSON-serializable question or record. Show the draft identifier, revision and proposed operation. Do not put a function or a live credential in that review payload.
  • Resume the intended thread. Keep a stable thread_id in the configurable run context. Resume using the documented Command(resume=...) mechanism and that same thread. A new thread identifier selects new state; it is not a way to recover the previous review.
  • Keep writes retry-safe. The interrupt documentation warns that the containing node can run again on resume. It also warns that a failure after an approved side effect commits can cause a retry to repeat that effect. Place the write behind the decision and make the fake destination deduplicate by the stable action_id. That deduplication is application behaviour you must implement and test.

Compare expected records, not polished answers

  1. Reject: resume with a rejection; expect zero destination receipts and no saved result.
  2. Approve: resume the unchanged revision; expect one receipt linked to its action identifier.
  3. Interrupt after the write: in the isolated fixture, stop after the fake destination commits but before the graph records completion. Resume and inspect the destination. Two records for one action identifier fail the exercise.
  4. Change the draft: alter the revision and confirm that an earlier approval cannot authorise the new content.

These were the original exercise criteria. The recorded trial below covers approval, rejection, revision-mismatched input and write-retry behavior. Editing an already approved draft in a live application remains a separate test, not a result claimed here. Save the input, chosen persistence backend, thread identifier, state changes and receipt count when you actually run the task. A checkpoint proves saved graph state; the destination record establishes whether a side effect occurred.

Make the smaller alternative compete

Compare this with a normal function and an explicit review record. Keep LangGraph if its state and resumption controls make your real workflow easier to operate; otherwise the simpler implementation may be sufficient. The core package license is MIT. Model integrations, databases and hosted services have separate operational and contractual terms. Neither a framework license nor a successful checkpoint is a certification of your application's permissions.

Our local SQLite checkpoint test

We ran a real compiled StateGraph, with ordinary Python functions as its nodes, on an arm64 Mac using Python 3.13.7 and SQLite 3.53.1. The installed versions were LangGraph 1.2.20, langgraph-checkpoint 4.3.1 and langgraph-checkpoint-sqlite 3.1.3. This tests the open-source runtime and local persistence, not an LLM, hosted product, social integration or production publishing system.

The input was a fixed fictional research draft at revision 1. Its graph prepared the draft, called interrupt() for a decision, then either ended or wrote to a disposable SQLite sink. An approval fixture carried its revision; our own comparison rejected a mismatched revision. That comparison is application code, not an identity or authorization service supplied by the framework.

We compiled with SqliteSaver, used a stable thread identifier and requested synchronous checkpoint durability. Each of the 15 actions ran in a new Python process. This deliberately discarded memory between calls: a later process had to reopen the checkpoint file and find the saved pending review. Tracing was disabled, no provider credentials were passed, and a runtime audit hook blocked DNS or socket connection attempts. It recorded zero such attempts.

Observed outcomes

  • Pause and restart: the initial calls stopped at review, exposed the fictional draft and left zero sink receipts. Reopening the database in another process recovered the pending review and its values.
  • Approval and rejection: approval of revision 1 completed with one receipt. Rejection ended with zero write attempts. Supplying an approval for revision 2 against the revision-1 draft also ended without a write.
  • Duplicate resume: another approval command after the completed run returned the completed state without adding a second attempt or receipt. This is the observed behavior of this graph and these versions; it does not prove that arbitrary repeated inputs or concurrent requests are safe.
  • Work before the interrupt: an intentionally misplaced local marker was written once on the initial call and again when the review node resumed. Its count rose from one to two. A checkpoint does not make the code preceding interrupt() execute only once.
  • Committed write followed by failure: we let the ordinary sink commit, then raised a deliberate exception before the node returned. A new process retried the unfinished save node. The sink contained two receipts for the same action. This is an observed duplicate effect, not a successful exactly-once claim.
  • Sink-owned deduplication: under the same injected failure, a separate sink table used the action identifier as a primary key. Recovery still made two attempts, but its conflict rule retained one accepted row. That property belongs to this SQLite operation and our stable key, not to every external destination.

All 15 checks passed, including assertions that expose the unsafe duplicate. A passing test therefore does not mean every fixture was safe. The process sequence took 16.333 seconds including interpreter startup, imports, graph compilation and local I/O. This single small run is not a throughput, server-sizing or model-quality benchmark.

Reproduce the trial before adopting the pattern

Download the sanitized observations JSON, executed Python script as text, complete version pins and reproduction instructions. The script download has exactly the SHA-256 recorded by the run. The data contains fixed fixtures, state snapshots, sink counts, errors, timings and checks; it contains no private database, host address or account data.

  1. Create an isolated Python 3.13 environment and install the supplied requirements. Package retrieval needs internet access; the workflow itself does not need a model key or service account.
  2. Save the script as langgraph_hands_on.py. Run it with run --output ./fresh-langgraph-run, supplying a directory that does not already exist. It writes disposable checkpoints, sink data and per-action records there.
  3. Inspect both the graph snapshots and the destination counts. In particular, look for two unsafe receipts versus one unique-key receipt after two attempts. Do not infer external effects from the graph's final state alone.

The restart test covers ordinary process exit and a controlled exception, not SIGKILL, power loss, corrupted checkpoints, concurrent writers, remote transactions or an authenticated reviewer. The installed components report MIT licenses; hosted services and other integrations have separate terms. Keep the simpler function-and-review-record alternative unless recoverable graph state solves a real operating problem. Before any live action, independently test reviewer permissions, draft edits, concurrency and the actual destination's retry contract.

Software repository and terms ↗

Suggest a correction

Continue reading