bytedance/deer-flow · error · HTTPException

Failed to read latest checkpoint

Error message

Failed to read latest checkpoint

What it means

Raised in _prepare_regenerate_payload (HTTP 500) when accessor.aget(latest_config) throws while reading the thread's latest checkpoint. It is a server-side checkpointer failure (storage unreachable, deserialization error), not caused by request payload.

Source

Thrown at backend/app/gateway/routers/thread_runs.py:637

            (candidate for candidate in records if getattr(candidate, "run_id", None) == source_run_id),
            None,
        )
    if record is None:
        return None
    if getattr(record, "thread_id", None) != thread_id:
        return None
    if _run_status_value(record) != RunStatus.interrupted.value:
        return None
    return source_run_id


async def _prepare_regenerate_payload(thread_id: str, message_id: str, request: Request) -> RegeneratePrepareResponse:
    accessor, latest_config = await build_thread_checkpoint_state_accessor(request, thread_id=thread_id)
    try:
        latest_checkpoint = await accessor.aget(latest_config)
    except Exception as exc:
        logger.exception("Failed to read latest checkpoint for regenerate thread %s", thread_id)
        raise HTTPException(status_code=500, detail="Failed to read latest checkpoint") from exc
    latest_checkpoint_id = _checkpoint_configurable(latest_checkpoint).get("checkpoint_id")
    if not latest_checkpoint_id:
        raise HTTPException(status_code=404, detail=f"Thread {thread_id} has no checkpoint")

    messages = _checkpoint_messages(latest_checkpoint)
    target_index = next((i for i, message in enumerate(messages) if _message_id(message) == message_id), None)
    if target_index is None:
        # A response interrupted during an LLM call can be visible in the live
        # stream without ever reaching a checkpoint. The server-stamped run ID
        # on the latest user message is the durable link to that partial turn.
        previous_human = next(
            (message for message in reversed(messages) if _is_visible_human_message(message)),
            None,
        )
        target_run_id = await _find_interrupted_target_run_id(thread_id, previous_human, request) if previous_human is not None else None
        if target_run_id is None:
            raise HTTPException(status_code=404, detail=f"Message {message_id} not found")
    else:

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Read the Gateway log exception 'Failed to read latest checkpoint for regenerate thread' for the root cause.
  2. Verify checkpointer backend health and credentials.
  3. Repair or clear the corrupted checkpoint for that thread if deserialization is the cause.
  4. Retry once the backend recovers.
Defensive patterns

Strategy: retry

Validate before calling

if ((await fetch('/api/health')).status !== 200) { deferCall(); }

Try / catch

try { await regeneratePrepare(threadId, messageId); } catch (e) { if (e.status === 500 && /read latest checkpoint/.test(e.detail)) { await backoffRetry(regeneratePrepare, threadId, messageId, 1); } else throw e; }

Prevention

When it happens

Trigger: Checkpoint DB outage or timeout during the regenerate-prepare call; corrupted checkpoint blob that fails deserialization; checkpointer backend restarted mid-request.

Common situations: Transient DB connectivity loss; checkpoint schema drift after an upgrade; misconfigured checkpointer in config.yaml.

Related errors


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