bytedance/deer-flow · error · HTTPException
Checkpoint is missing checkpoint_id
Error message
Checkpoint is missing checkpoint_id
What it means
Raised by _checkpoint_response (HTTP 409) when a LangGraph checkpoint tuple's configurable dict has no 'checkpoint_id'. The router builds the regenerate checkpoint response from checkpoint_configurable(); a checkpoint without an id cannot be addressed for replay, so the operation is refused. It indicates the checkpoint tuple passed in is empty (e.g. a thread with no history) or came from a malformed/degenerate checkpoint write.
Source
Thrown at backend/app/gateway/routers/thread_runs.py:373
def _checkpoint_messages(snapshot: Any) -> list[Any]:
return checkpoint_messages(snapshot)
def _checkpoint_values(snapshot: Any) -> dict[str, Any]:
values = getattr(snapshot, "values", None)
return dict(values) if isinstance(values, dict) else {}
def _checkpoint_configurable(checkpoint_tuple: Any) -> dict[str, Any]:
return checkpoint_configurable(checkpoint_tuple)
def _checkpoint_response(checkpoint_tuple: Any) -> dict[str, Any]:
configurable = _checkpoint_configurable(checkpoint_tuple)
checkpoint_id = configurable.get("checkpoint_id")
if not checkpoint_id:
raise HTTPException(status_code=409, detail="Checkpoint is missing checkpoint_id")
return {
"checkpoint_ns": str(configurable.get("checkpoint_ns") or ""),
"checkpoint_id": str(checkpoint_id),
"checkpoint_map": configurable.get("checkpoint_map"),
}
def _clean_human_message_for_regenerate(message: Any) -> dict[str, Any]:
additional_kwargs = _message_additional_kwargs(message)
content = get_original_user_content_text(_message_content(message), additional_kwargs)
additional_kwargs.pop(ORIGINAL_USER_CONTENT_KEY, None)
additional_kwargs.pop("hide_from_ui", None)
clean_message: dict[str, Any] = {
"type": "human",
"content": [{"type": "text", "text": content}],
"additional_kwargs": additional_kwargs,
}View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Verify the thread actually has runs: GET the thread's state/run history and confirm at least one completed run exists before regenerating.
- Inspect the checkpoint store directly (e.g. the checkpoints table / checkpointer list) for the thread_id to confirm checkpoint rows carry a valid checkpoint_id.
- If a custom checkpointer is used, ensure put() persists configurable['checkpoint_id'] and aget() returns full tuples.
- Re-run the conversation once so a fresh, well-formed checkpoint is written, then retry regenerate.
Example fix
// before
const res = await fetch(`/api/thread/${threadId}/runs/regenerate-prepare`, {method: 'POST'});
// after
const state = await fetch(`/api/threads/${threadId}/state`).then(r => r.json());
if (!state.checkpoint?.checkpoint_id) throw new Error('Thread has no addressable checkpoint yet');
const res = await fetch(`/api/thread/${threadId}/runs/regenerate-prepare`, {method: 'POST'}); Defensive patterns
Strategy: validation
Validate before calling
const state = await getThreadState(threadId);
if (!state?.checkpoint?.checkpoint_id) {
throw new Error('Thread has no addressable checkpoint; run a message first');
} Type guard
function hasAddressableCheckpoint(state: unknown): state is { checkpoint: { checkpoint_id: string } } {
return !!state && typeof state === 'object'
&& typeof (state as any).checkpoint?.checkpoint_id === 'string'
&& (state as any).checkpoint.checkpoint_id.length > 0;
} Try / catch
try { await regeneratePrepare(threadId, messageId); } catch (e) { if (e.status === 409 && /missing checkpoint_id/.test(e.detail)) { showInfo('Nothing to regenerate yet'); } else { throw e; } } Prevention
- Only offer regenerate after a completed run
- Fetch thread state and require checkpoint_id before showing regenerate UI
When it happens
Trigger: Calling the regenerate prepare endpoint on a thread whose latest/base checkpoint tuple resolves to None or has an empty configurable; a checkpoint store that returns tuples without thread_ts/checkpoint_id; replaying against a thread whose checkpoints were pruned mid-operation.
Common situations: Regenerating immediately after thread creation before any run checkpointed; a custom checkpointer that drops configurable fields; checkpoint DB rows deleted between the history scan and the aget call; older checkpoints written by a different LangGraph version with a different configurable schema.
Related errors
- Could not safely resolve the checkpoint before the target us
- Could not find an addressable checkpoint before the target u
- Could not locate target user message in recent checkpoint hi
- Only the latest completed user turn can be edited
- Only completed assistant text turns can be edited
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/2dba89db9b77bed9.
Report an issue: GitHub.