DataAIHub
DataAIHubNews · Research · Tools · Learning
AI Agents

Durable Execution Guide

Checkpointing, persistence, retries, and resumable workflows for long-running AI agents - so crashes, timeouts, and human waits do not lose progress.

55 min readIntermediateUpdated Jul 16, 2026

Quick Summary

Durable execution persists agent workflow state so runs can pause, crash, retry, and resume without losing completed steps.

One Analogy

Like saving a game at every level checkpoint - if the power fails mid-boss-fight, you reload at the last save instead of restarting the campaign.

Engineering Rule

Treat every multi-step agent run as a resumable workflow: externalize state, make side effects idempotent, and never rely on an in-memory while-loop alone in production.

TL;DR

  • Durable execution means agent runs survive process death - state is checkpointed so work resumes from the last safe step, not from scratch.

  • Checkpointing + idempotent side effects are the core pair: persistence without idempotency causes duplicate refunds and double emails on retry.

  • Long-running agents need this by default - human approvals, slow tools, rate limits, and multi-hour research all outlive a single request.

  • Retries must be scoped - retry LLM/tool failures with backoff; do not blindly replay irreversible writes.

  • Pick the durability layer intentionally - LangGraph checkpointers for agent graphs, Temporal/workflow engines for long-lived business processes, or both composed.

Why This Matters

Agent demos run in one process for thirty seconds. Production agents wait for humans, hit API rate limits, deploy across rolling restarts, and run for minutes to days. An in-memory for step in range(n) loop loses everything when the worker dies mid-flight.

Durable execution is the difference between "we have an agent prototype" and "we can operate agents like any other workflow." It also unlocks human-in-the-loop: approvals only work if the run can suspend for hours and resume with the same context.

Cost is another driver. Replaying five completed tool calls and three LLM turns after a crash wastes money and can re-trigger side effects. Checkpoints let you pay for the remaining work only.

The Problem Durable Execution Solves

Without durability, multi-step agents fail when:

  1. The worker process restarts - deploys, OOM kills, spot instance preemption.

  2. A step waits longer than the HTTP timeout - human approval, batch job, slow vendor API.

  3. A transient error hits mid-run - 429 rate limit, brief DB blip - and naive retry restarts the whole task.

  4. You need auditability - regulators and incident response ask "what state were we in when it failed?"

  5. Multiple workers must coordinate - only one should resume a given thread_id / workflow ID.

These are classic distributed-systems problems. Agents amplify them because each step is expensive (LLM tokens) and sometimes irreversible (tools with side effects). Durable execution applies workflow-engine ideas - checkpoints, signals, retries - to agent graphs.

How We Got Here

Agent durability reused decades of workflow engineering, then specialized for non-deterministic LLM steps:

Diagram: Evolution of durable execution

flowchart LR
    A[Cron / scripts] --> B[Job queues]
    B --> C[Saga / BPMN engines]
    C --> D[Temporal-style durable workflows]
    D --> E[Agent checkpointers]
    E --> F[HITL + multi-day runs]

Agent checkpointers reuse workflow-engine ideas so LLM steps survive crashes and human waits.

Era What persisted Gap for agents
Scripts & cron Nothing mid-run Crash = restart from zero
Job queues Task payload No graph cursor or tool history
Saga / BPMN Business process state Weak open-ended LLM planning
Temporal durable execution Workflow history + activities LLM steps need recorded outputs, not blind replay
Agent checkpointers Graph state + messages (LangGraph persistence) Must pair with idempotent tools

Anthropic — Building effective agents stresses reliable orchestration over unbounded autonomy; durability is how that reliability survives deploys and human waits.

What Is Durable Execution?

Durable execution is a runtime property: a workflow's progress is persisted such that execution can continue after failures, pauses, or migrations without losing completed steps.

For AI agents, that usually means:

Concept Meaning
Checkpoint Snapshot of graph/agent state after a step (messages, counters, pending tool calls)
Thread / run ID Stable identity for the workflow across resumes
Replay / resume Continue from the latest checkpoint with new input or retry
Idempotency Safe re-execution of a step that might have partially completed
Timer / signal Wake on timeout or external event (approval, webhook)

Durable execution is related to but not identical to agent memory. Memory stores knowledge across sessions; durability stores execution progress within a run (and sometimes across). You often need both.

Engineering Insight

💡 Key Idea - Durability is about the control plane of the agent (where am I in the graph?), not the intelligence of the model. A smarter model does not survive a crashed worker; a checkpoint does.

How Durable Execution Works

At a high level, the orchestrator wraps each step:

  1. Load latest checkpoint for run_id.
  2. Execute the next node (LLM call, tool, router).
  3. Persist a new checkpoint (atomically with step completion metadata).
  4. On failure, decide: retry step, compensate, or wait for signal.

Diagram: Interaction sequence

sequenceDiagram
    participant Client
    participant Worker
    participant Checkpointer
    participant Tool

    Client->>Worker: start / resume(run_id)
    Worker->>Checkpointer: load checkpoint
    Checkpointer-->>Worker: state @ step N
    Worker->>Worker: LLM / decide
    Worker->>Tool: execute (idempotency key)
    Tool-->>Worker: result
    Worker->>Checkpointer: save checkpoint N+1
    Note over Worker,Checkpointer: Process crash here is safe
    Client->>Worker: resume(run_id)
    Worker->>Checkpointer: load checkpoint N+1
    Worker->>Worker: continue from N+1

Sequence of messages and tool calls for this pattern.

Run lifecycle

Diagram: State lifecycle

stateDiagram-v2
    [*] --> Created
    Created --> Running: start
    Running --> Running: step + checkpoint
    Running --> Waiting: HITL / signal / timer
    Waiting --> Running: resume / signal
    Running --> Retrying: transient failure
    Retrying --> Running: backoff ok
    Retrying --> Failed: exhausted
    Running --> Completed: terminal
    Running --> Failed: poison / bug
    Completed --> [*]
    Failed --> [*]

Valid states and transitions for this control-plane pattern.

Lifecycle notes: Waiting must outlive the worker process. Retries apply to the current step with idempotency - not a full replay of completed tools. Graph version checks prevent new deploys from resuming into missing nodes.

Checkpoint contents

A minimal agent checkpoint includes:

  • Message / event history (or pointers to blob storage for large payloads)
  • Graph cursor (current node, pending edges)
  • Structured state fields (order_id, plan, budgets)
  • Interrupt payloads awaiting HITL
  • Version of graph definition (so code deploys do not silently mismatch state)

Retry policies

Failure Typical policy
LLM 5xx / timeout Retry with exponential backoff + jitter
Tool 429 Retry-After aware backoff
Tool 4xx validation Do not retry blindly; return error to agent to replan
Side effect unknown outcome Reconcile with idempotency key / status API before retry
Deterministic bug Fail the run; do not infinite retry

Architecture

Diagram: Durable agent run architecture

flowchart TB
    Client[Client / queue] --> Worker[Stateless worker]
    Worker --> Graph[Agent graph]
    Worker --> Runtime[Durable runtime]
    Runtime --> Store[(Checkpoint store)]
    Graph --> LLM[LLM calls]
    Graph --> Tools[Tool adapters]
    Tools --> Ledger[Idempotency ledger]
    Ledger --> Ext[External APIs]
    Runtime --> Lease[Run lease / lock]
    Worker --> Obs[Traces / metrics]

Major components and how control or data moves between them.

Layer Role Examples
Agent graph Business logic: nodes, tools, routers LangGraph, CrewAI flows, custom FSM
Durable runtime Checkpoint, timers, worker leasing LangGraph checkpointer, Temporal, Inngest
State store Durable persistence Postgres, Redis, S3 for large blobs
Side-effect gate Idempotent adapters around tools Payment APIs with idempotency keys
Observability Correlate run_id → steps → costs LangSmith, OpenTelemetry

Two common compositions:

  1. Agent-native durability - LangGraph + Postgres checkpointer. Best when the graph is the product.
  2. Workflow engine + LLM steps - Temporal workflow calls activities that invoke the LLM/tools. Best when the business process already lives in Temporal and agents are activities inside it.

Best Practice

Best Practices - Start with a Postgres-backed checkpointer and idempotent write tools. Add a full workflow engine when you need calendars, multi-day SLAs, or complex compensation sagas.

Step-by-Step Flow

Research agent task: "Compile a competitive brief on three vendors; wait for PM approval; then email the doc."

  1. Create run - Allocate run_id, persist initial state {goal, vendors[]}.

  2. Research steps - For each vendor, call search/browse tools; checkpoint after each vendor so a crash mid-loop does not discard prior research.

  3. Draft - LLM synthesizes brief; checkpoint stores draft blob reference (not a 50KB inline row if avoidable).

  4. Interrupt for approval - HITL gate; worker releases lease. State remains durable.

  5. Hours later - PM approves via UI; API signals resume with decision.

  6. Worker picks up run - Loads checkpoint; skips research (already done); proceeds to email activity.

  7. Email with idempotency key - email:{run_id}:brief so a double resume cannot spam.

  8. Terminal checkpoint - Mark run completed; retain for audit retention policy.

  9. On deploy mid-run - New workers resume old run_ids; graph version check ensures compatibility or migrates state.

Real Production Example

A compact pattern: checkpointed loop with idempotent tools and resume after failure.

from __future__ import annotations

import hashlib
import json
import time
from dataclasses import dataclass, field
from typing import Any, Callable

@dataclass
class Checkpoint:
    run_id: str
    step: int
    state: dict[str, Any]
    status: str  # "running" | "waiting" | "completed" | "failed"

class CheckpointStore:
    """Replace with Postgres in production."""

    def __init__(self):
        self._data: dict[str, Checkpoint] = {}

    def load(self, run_id: str) -> Checkpoint | None:
        return self._data.get(run_id)

    def save(self, cp: Checkpoint) -> None:
        self._data[run_id := cp.run_id] = cp

def idempotency_key(run_id: str, tool: str, args: dict) -> str:
    payload = json.dumps({"run_id": run_id, "tool": tool, "args": args}, sort_keys=True)
    return hashlib.sha256(payload.encode()).hexdigest()[:32]

@dataclass
class DurableAgent:
    store: CheckpointStore
    llm: Callable[[dict], dict]
    tools: dict[str, Callable[..., str]]
    max_steps: int = 12

    def start(self, run_id: str, goal: str) -> Checkpoint:
        cp = Checkpoint(run_id=run_id, step=0, state={"goal": goal, "messages": [], "results": []}, status="running")
        self.store.save(cp)
        return self._run(cp)

    def resume(self, run_id: str, signal: dict | None = None) -> Checkpoint:
        cp = self.store.load(run_id)
        if cp is None:
            raise KeyError(f"unknown run {run_id}")
        if signal:
            cp.state["signal"] = signal
            cp.status = "running"
        return self._run(cp)

    def _run(self, cp: Checkpoint) -> Checkpoint:
        while cp.status == "running" and cp.step < self.max_steps:
            action = self.llm(cp.state)

            if action["type"] == "wait_for_human":
                cp.state["interrupt"] = action["payload"]
                cp.status = "waiting"
                self.store.save(cp)
                return cp

            if action["type"] == "final":
                cp.state["answer"] = action["content"]
                cp.status = "completed"
                self.store.save(cp)
                return cp

            if action["type"] == "tool":
                key = idempotency_key(cp.run_id, action["name"], action["args"])
                result = self._exec_tool(action["name"], action["args"], key)
                cp.state["results"].append({"tool": action["name"], "result": result, "key": key})
                cp.state["messages"].append({"role": "tool", "content": result})
                cp.step += 1
                self.store.save(cp)  # durable boundary after each tool
                continue

            cp.status = "failed"
            cp.state["error"] = f"unknown action {action}"
            self.store.save(cp)
            return cp

        if cp.step >= self.max_steps and cp.status == "running":
            cp.status = "failed"
            cp.state["error"] = "max_steps exceeded"
            self.store.save(cp)
        return cp

    def _exec_tool(self, name: str, args: dict, key: str) -> str:
        # Production: consult a side-effect ledger keyed by `key` before calling out.
        return self.tools[name](**args, idempotency_key=key)

# --- LangGraph-shaped sketch (production) ---
# from langgraph.checkpoint.postgres import PostgresSaver
# with PostgresSaver.from_conn_string(DATABASE_URL) as checkpointer:
#     app = graph.compile(checkpointer=checkpointer)
#     app.invoke(input, {"configurable": {"thread_id": run_id}})
#     # later / after crash:
#     app.invoke(None, {"configurable": {"thread_id": run_id}})  # or Command(resume=...)

The important boundaries: save after each side-effecting step, suspend cleanly for humans, and key writes so resume cannot double-apply.

Design Decisions

Decision Option A Option B When to choose
Durability scope Checkpoint agent graph only Full Temporal/workflow engine Graph-only for agent products; engine when SLAs/sagas dominate
Storage Redis Postgres Redis for short TTL; Postgres for audit + multi-day runs
Checkpoint granularity After every node After writes only Every node for safety; writes-only if checkpoint volume hurts
Payload size Inline JSON Object store pointers Pointers when messages/tools return large blobs
Retry ownership Orchestrator retries tools Agent replans on error Orchestrator for transient infra; agent for semantic errors
Exactly-once At-least-once + idempotency True exactly-once broker Prefer idempotency - exactly-once is rarely free

Comparisons

Durable Execution vs Retries

Dimension Durable execution Retries alone
Survives process death Yes (checkpoint) No - in-memory loop is gone
Skips completed work Resume from last safe step Often restarts the whole run
Side-effect safety Needs idempotency + ledger Blind retry can double-write
Human waits First-class waiting status HTTP timeout / lost context
When enough Multi-step / long-lived Single call with transient 5xx

Retries are a policy inside durability. Exponential backoff without checkpoints still loses the run on deploy. See Temporal durable execution for the classic workflow framing.

Durable Execution vs Agent Memory

Dimension Durable execution Agent memory
Question answered Where am I in this run? What do I know across sessions?
Typical store Checkpointer / workflow history Vector DB, profiles, facts
Failure mode if missing Replay from scratch / lost interrupt Cold start; re-ask the user
Overlap May persist messages Messages ≠ graph cursor

You need both for production agents: agent memory for knowledge, durability for control-plane progress. Confusing them is a common design bug.

Durable Execution vs HITL

Dimension Durable execution HITL
Primary job Survive pause/crash/retry Get a human decision before a write
Without the other Can resume but still auto-execute risk Approvals arrive to a dead process
Shared artifact Checkpoint + run_id Interrupt payload + decision
Compose Durability enables HITL HITL is a common reason to wait

Decision tree: do you need durability?

Decision tree: When to add durable execution

flowchart TD
    A[Multi-step agent or workflow?] -->|No| B[Stateless request is enough]
    A -->|Yes| C{Can the run outlive one process?}
    C -->|No| B
    C -->|Yes| D[Checkpoint after safe steps]
    D --> E{Human approval or long waits?}
    E -->|Yes| F[Durable status + resume API]
    E -->|No| G[Still checkpoint for deploys/retries]
    F --> H[Pair with idempotent side effects]
    G --> H

If a deploy mid-run would force a full replay, you need checkpoints - not just retries.

Human-in-the-loop is the product feature; durable execution is the infrastructure that makes interrupt/resume real - documented clearly in LangGraph HITL + persistence.

⚠ Common Mistakes

  1. In-memory state in production - Works until the first deploy during a run.

  2. Checkpointing without idempotent tools - Resume duplicates payments and emails.

  3. Giant checkpoints - Stuffing full scraped HTML into Postgres rows blows latency and storage. Store references.

  4. Retrying non-idempotent activities blindly - Distinguish transient infra failures from "already succeeded, response lost."

  5. Ignoring graph version skew - Deploying new node names while old runs resume into missing nodes.

  6. Confusing memory with durability - Vector memory does not resume a half-finished refund workflow.

  7. One global worker without leasing - Two workers resume the same run_id and race side effects.

Where It Breaks Down

  • Ultra-low-latency single-shot tools - Durability overhead is unnecessary for one LLM call with no side effects.

  • Non-deterministic replay assumptions - Some workflow engines replay history deterministically; LLM calls are not deterministic. Prefer checkpoint-and-continue over full event replay for model steps, or record model outputs as history events.

  • Huge fan-out - Millions of concurrent short runs may need a lighter store or batching strategy.

  • Poison messages - A bad state that crashes every resume needs a dead-letter path and manual repair tools.

  • Cross-system transactions - Durability of the agent does not make a multi-API business operation atomic. Use sagas/compensations.

When NOT to Use Durable Execution

Skip a full durability layer when:

  1. Single-shot, no side effects - one LLM call or one read-only tool; crash cost is a cheap retry.
  2. Runs always finish inside one request - no HITL, no multi-minute tools, no deploy mid-flight risk you care about.
  3. You cannot make writes idempotent - checkpoints without idempotency keys make duplicates more likely on resume.
  4. Checkpoint payloads would be huge and unscoped - scraping full HTML into every step; redesign blobs/pointers first.
  5. A parent workflow engine already owns the saga - e.g. Temporal activities wrap the LLM; do not double-persist conflicting cursors.
  6. Latency budget is tighter than checkpoint I/O and you have no multi-step risk - rare; usually LLM time dominates.

Otherwise prefer agent-native checkpointers (LangGraph) or a workflow engine - compare orchestration options in Best AI Agent Frameworks.

Running in Production

Best Practice

Best Practices - Persist after every side effect, lease runs to one worker, version your graphs, keep checkpoints lean, and wrap external writes with idempotency keys.

Dimension Consideration
Scaling Stateless workers + shared checkpointer. Partition by run_id. Autoscale on queue depth.
Latency Checkpoint write adds ~5–50ms on Postgres. Dominated by LLM/tool time. Batch non-critical saves carefully.
Cost Avoid full replays. Storage cost is usually tiny vs. token waste from restarting runs.
Monitoring Open runs by age, resume count, checkpoint size, retry storms, zombie waiting runs.
Evaluation Chaos-test: kill workers mid-run; assert no duplicate side effects and correct resume.
Security Encrypt checkpoints at rest if they hold PII; RBAC on resume/signal APIs.

Important

At-least-once delivery is the realistic default. Design every tool adapter as if the previous attempt might have succeeded. Query provider state or use idempotency keys before creating a new side effect.

Common Mistake

Using MemorySaver (or equivalent) in production "temporarily." It will be forgotten until an outage deletes every in-flight agent. Configure Postgres/Redis checkpointers before the first real user traffic.

  • LangGraph - Native checkpointers (PostgresSaver, SqliteSaver) and thread-based resume; pairs cleanly with HITL interrupts.
  • Temporal - General-purpose durable workflows; use when agents are steps inside longer business processes.
  • CrewAI - Crew/flow persistence patterns for multi-step agent teams.
  • AutoGen - Conversational multi-agent runs that need external persistence for production.
  • OpenAI Agents SDK - Session/run persistence hooks depending on deployment model.

Compare agent orchestration stacks:

If you understood this topic, read next:

Diagram: Learning path for durable agents

flowchart LR
    A[Durable] --> B[HITL]
    B --> C[Handoffs]
    C --> D[Memory]
    D --> E[Workflows]

Prerequisites: AI Agents · Workflows vs Agents

Next topics: Human-in-the-Loop · Agent Memory · Multi-Agent Handoffs

Estimated time: 55 min · Difficulty: Intermediate

Key Takeaways

  • Durable execution persists control-plane progress so crashes, deploys, and human waits do not wipe completed steps.
  • Checkpoint after side effects; pair with idempotent tools - persistence alone is not safe.
  • Retries are a policy; they do not replace checkpoints. Agent memory is knowledge, not a run cursor.
  • HITL depends on durability: approvals arrive minutes later to a resumed run_id.
  • Choose agent-native checkpointers (LangGraph) or engines like Temporal based on who owns the business saga.
  • Version graphs, lease runs to one worker, and chaos-test kill/resume for duplicate side effects.
  • Compare stacks via Best AI Agent Frameworks and LangGraph vs CrewAI.

FAQs

Is durable execution the same as storing chat history?

No. Chat history is one field inside state. Durability also tracks graph position, interrupts, retries, and run lifecycle so execution can continue correctly.

Do I need Temporal if I use LangGraph checkpointers?

Not always. LangGraph persistence covers many agent apps. Choose Temporal when you already standardize on it for business workflows, need complex timers/sagas, or run multi-day processes with strict ops tooling.

What should I checkpoint after?

At minimum after every side-effecting tool call. After every node is safer for long reasoning chains.

How do I prevent duplicate side effects on resume?

Idempotency keys, upsert semantics, and status checks against the external system before creating new resources.

Can I change my graph code while runs are in flight?

Yes with care: version graphs, avoid deleting nodes that in-flight runs still need, and migrate state when schemas change.

How long should checkpoints be retained?

Match audit and debugging needs - often 30–90 days for production agents. Separate hot (active runs) from cold (completed) storage if volume grows.

What about secrets in checkpoints?

Do not persist raw API keys or credentials. Redact tool args that contain secrets; store references to a secret manager.

How does this relate to serverless timeouts?

Durable execution is how you survive 15–60s function limits: each invocation loads state, runs one or few steps, saves, exits; a queue continues the run.

Should LLM calls be deterministic for replay?

Prefer recording the LLM output in the checkpoint and continuing forward, rather than re-calling the model during "replay" of history.

What metrics indicate durability problems?

Spike in duplicate side effects, rising average resumes per run, checkpoint write errors, and stuck waiting runs beyond SLA.

Is Redis durable enough?

With AOF/replication and short-lived runs, often yes. For financial workflows and long HITL waits, prefer Postgres or a workflow engine's store.

How do multi-agent systems checkpoint?

Either one shared run state with sub-agent spans, or child workflows with parent orchestration. Always propagate a correlation run_id.

Does durability make agents slower?

Slightly per step; massively faster/cheaper after failures because you do not redo completed work.

What's the first production checklist item?

Replace in-memory savers, add thread_id/run_id to every invoke, and wrap the top three write tools with idempotency.

References

Further Reading

Next Topics

Learning Path

Continue Learning

Related Guides

Related Tools

ToolCategoryPurposeWebsiteBest For
LangGraph
FeaturedOpen SourceAPI
frameworksGraph-based orchestration runtime for long-running, stateful agents.langgraph.devMulti-agent orchestration
Temporal
Open SourceAPI
automationDurable execution platform for reliable long-running workflows and microservices.temporal.ioLong-running business processes
CrewAI
NewOpen SourceAPI
frameworksMulti-agent framework with Crews, tasks, and event-driven Flows.crewai.comContent pipelines
AutoGen
Open SourceAPI
frameworksMicrosoft framework with AgentChat, Core, Extensions, and Studio for multi-agent systems.microsoft.github.ioConversational multi-agent apps
OpenAI Agents SDK
Open SourceAPI
frameworksOfficial OpenAI framework for tool-using agents with handoffs, guardrails, and tracing.openai.github.ioMulti-step agent workflows
Agno
Open SourceAPI
frameworksPython framework and AgentOS runtime for agents, teams, and workflows.agno.comAgent platforms

Related Rankings

Related Comparisons