langflow-ai/langflow · error · HTTPException
Checkpoint expired or not found; cannot resume this run.
Error message
Checkpoint expired or not found; cannot resume this run.
What it means
Raised as HTTP 404 when a HITL (human-in-the-loop) resume request references a run whose checkpoint no longer exists in the checkpoint store. The comment in the source explains why: re-dispatching with resume/job_id still set would recurse to RecursionError, so a missing or expired checkpoint is treated as unrecoverable and surfaced as a clean 404.
Source
Thrown at src/backend/base/langflow/api/build.py:481
async def build_resumed_graph_and_get_order() -> tuple[list[str], list[str], Graph]:
"""Resume a suspended HITL run from its durable checkpoint instead of building fresh.
Hydrates the graph, injects the human decision keyed by request_id, and un-builds
the paused node so it re-runs and routes (its first-run output was a placeholder).
"""
from lfx.graph.graph.base import Graph as LfxGraph
from lfx.run.hitl import request_id_targets_vertex
from lfx.services.deps import get_checkpoint_service
from langflow.api.v2.hitl import reroute_decision_on_timeout
run_id = str(job_id)
store = get_checkpoint_service()
checkpoint = await store.load_by_run_id(run_id)
if checkpoint is None:
# Why: re-dispatching here (resume/job_id still set) recurses to RecursionError; a missing
# or expired checkpoint is unrecoverable, so surface a clean 404 instead.
raise HTTPException(status_code=404, detail="Checkpoint expired or not found; cannot resume this run.")
graph = LfxGraph.resume_from_checkpoint(checkpoint, checkpoint_store=store)
if not graph.user_id:
graph.user_id = str(current_user.id)
# Resume skips the initial run's trace setup (trace_context_var stays unset → post-pause
# vertices like Chat Output never trace); re-init so the resumed vertices trace.
graph.flow_name = graph.flow_name or flow_name
await graph.initialize_run()
pending = await get_job_service().get_pending_human_request(job_id)
decision = reroute_decision_on_timeout(pending, resume["decision"])
# Merge with checkpoint-restored decisions so a re-run HITL keeps its answer (no multi-HITL loop).
graph.human_input_decisions = {
**(getattr(graph, "human_input_decisions", {}) or {}),
resume["request_id"]: decision,
}
action_id = str((decision or {}).get("action_id", ""))
gate_label = _hitl_gate_label(action_id, (pending or {}).get("options"))
if graph.tracing_service:
graph.tracing_service.record_event_span(View on GitHub (pinned to 976ec789d2)
Solutions
- Check the run's checkpoint exists before offering a resume button (GET pending human request / checkpoint lookup)
- If checkpoints are in-memory, switch to a persistent checkpoint service (database-backed) so restarts do not lose state
- Resume promptly or raise checkpoint TTL so paused runs survive
- If already unrecoverable, start a new run instead of resuming; the 404 is intentional to avoid infinite re-dispatch recursion
Example fix
// before
await client.resumeRun(jobId, { decision: 'approve' }); // 404 after restart
// after
const pending = await client.getPendingRequest(jobId);
if (!pending) {
// checkpoint gone: start a fresh run
await client.runFlow(flowId);
} else {
await client.resumeRun(jobId, { decision: 'approve' });
} Defensive patterns
Strategy: validation
Validate before calling
const pending = await client.getPendingHumanRequest(jobId);
const cp = await checkpointService.loadByRunId(jobId);
if (!pending || !cp) { /* start a new run, do not resume */ } Try / catch
try { await resumeRun(jobId, decision) } catch (e) { if (e.status === 404 && /Checkpoint expired/.test(e.detail)) { await startNewRun(); } else throw e; } Prevention
- Use a persistent (DB-backed) checkpoint store in multi-replica deployments
- Check pending human request + checkpoint existence before showing a resume UI
- Resume HITL requests promptly; treat checkpoint TTL as a hard SLA
When it happens
Trigger: Calling the resume endpoint (POST with a resume payload and job_id) for a run whose checkpoint was evicted, expired, or never persisted; restarting the server with an in-memory checkpoint store; resuming after the checkpoint TTL elapsed.
Common situations: Long-paused HITL runs, server restarts wiping in-memory checkpoints, multiple replicas with non-shared checkpoint storage, or retrying a resume after the underlying job was already completed or cleaned up.
Related errors
- Failed to load file (HTTP ${status})
- reload-in-progress
- An error occurred while uploading the file
- Flow not found
- Build job not found
AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14).
Data as JSON: /api/errors/800ed09df563db4d.
Report an issue: GitHub.