bytedance/deer-flow · error · HTTPException
Failed to get thread
Error message
Failed to get thread
What it means
500 from GET /threads/{thread_id} when reading the checkpoint snapshot fails: accessor.aget(config) or the follow-up _fetch_raw_pending_writes raises a non-checkpoint-mode exception. Checkpoint-mode errors get their own mapping; everything else (storage failure, deserialization) becomes 'Failed to get thread' with a logged traceback.
Source
Thrown at backend/app/gateway/routers/threads.py:1048
record: dict | None = await thread_store.get(thread_id)
try:
accessor, config = build_checkpoint_state_accessor(
request,
thread_id=thread_id,
assistant_id=record.get("assistant_id") if record is not None else None,
)
except _CHECKPOINT_MODE_ERRORS as exc:
raise _checkpoint_mode_http_error(exc, thread_id) from exc
try:
snapshot = await accessor.aget(config)
checkpoint_id = (snapshot.config or {}).get("configurable", {}).get("checkpoint_id")
pending_writes = await _fetch_raw_pending_writes(checkpointer, snapshot.config) if checkpoint_id else []
except _CHECKPOINT_MODE_ERRORS as exc:
raise _checkpoint_mode_http_error(exc, thread_id) from exc
except Exception:
logger.exception("Failed to get checkpoint for thread %s", sanitize_log_param(thread_id))
raise HTTPException(status_code=500, detail="Failed to get thread")
if record is None and not checkpoint_id:
raise HTTPException(status_code=404, detail=f"Thread {thread_id} not found")
metadata = snapshot.metadata or {}
if record is None:
record = {
"thread_id": thread_id,
"status": "idle",
"created_at": coerce_iso(snapshot.created_at or metadata.get("created_at", "")),
"updated_at": coerce_iso(metadata.get("updated_at", snapshot.created_at or metadata.get("created_at", ""))),
"metadata": {key: value for key, value in metadata.items() if key not in ("created_at", "updated_at", "step", "source", "writes", "parents")},
}
stored_status = record.get("status", "idle")
status = _derive_thread_status(snapshot, pending_writes, fallback_status=stored_status) if checkpoint_id else stored_status
return ThreadResponse(
thread_id=thread_id,View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Read the 'Failed to get checkpoint for thread %s' traceback to see whether it is deserialization or connectivity.
- For deserialization failures, align the checkpointer/serializer versions with what wrote the data, or migrate old blobs.
- For connectivity, restore DB health; the GET is read-only and safe to retry.
- If one specific thread is corrupt, other threads remain readable — isolate and, if needed, delete the damaged thread.
Defensive patterns
Strategy: retry
Try / catch
try { const t = await api.get(`/api/threads/${id}`); }
catch (err) {
if (err.status === 500) { await backoff(); retryOnce(); } // transient storage blips self-heal
else throw err; // persistent 500 on one thread only => corrupt checkpoint, isolate it
} Prevention
- Align checkpointer and serializer versions before upgrading (old blobs must stay readable).
- Distinguish thread-specific persistent 500s (corruption) from global ones (outage) before retrying.
- Back up checkpoint storage before version upgrades.
When it happens
Trigger: GET a thread whose checkpoint blob fails to deserialize (corrupt row, serializer version change), or while checkpoint storage is unreachable/locked.
Common situations: Upgrading langgraph-checkpoint without migrating blobs; truncated/corrupt checkpoint rows after a crash; DB connectivity blips; concurrent write holding a lock on checkpoint tables.
Related errors
- Failed to create thread checkpoint
- Failed to create branch
- Failed to create thread
- Failed to update thread
- Failed to read thread goal
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/365ddb78c84c3501.
Report an issue: GitHub.