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

  1. Verify the thread actually has runs: GET the thread's state/run history and confirm at least one completed run exists before regenerating.
  2. 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.
  3. If a custom checkpointer is used, ensure put() persists configurable['checkpoint_id'] and aget() returns full tuples.
  4. 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

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


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/2dba89db9b77bed9. Report an issue: GitHub.