{"record":{"id":"365ddb78c84c3501","repo":"bytedance/deer-flow","slug":"failed-to-get-thread","errorCode":null,"errorMessage":"Failed to get thread","messagePattern":"Failed to get thread","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"backend/app/gateway/routers/threads.py","lineNumber":1048,"sourceCode":"    record: dict | None = await thread_store.get(thread_id)\n    try:\n        accessor, config = build_checkpoint_state_accessor(\n            request,\n            thread_id=thread_id,\n            assistant_id=record.get(\"assistant_id\") if record is not None else None,\n        )\n    except _CHECKPOINT_MODE_ERRORS as exc:\n        raise _checkpoint_mode_http_error(exc, thread_id) from exc\n\n    try:\n        snapshot = await accessor.aget(config)\n        checkpoint_id = (snapshot.config or {}).get(\"configurable\", {}).get(\"checkpoint_id\")\n        pending_writes = await _fetch_raw_pending_writes(checkpointer, snapshot.config) if checkpoint_id else []\n    except _CHECKPOINT_MODE_ERRORS as exc:\n        raise _checkpoint_mode_http_error(exc, thread_id) from exc\n    except Exception:\n        logger.exception(\"Failed to get checkpoint for thread %s\", sanitize_log_param(thread_id))\n        raise HTTPException(status_code=500, detail=\"Failed to get thread\")\n\n    if record is None and not checkpoint_id:\n        raise HTTPException(status_code=404, detail=f\"Thread {thread_id} not found\")\n\n    metadata = snapshot.metadata or {}\n    if record is None:\n        record = {\n            \"thread_id\": thread_id,\n            \"status\": \"idle\",\n            \"created_at\": coerce_iso(snapshot.created_at or metadata.get(\"created_at\", \"\")),\n            \"updated_at\": coerce_iso(metadata.get(\"updated_at\", snapshot.created_at or metadata.get(\"created_at\", \"\"))),\n            \"metadata\": {key: value for key, value in metadata.items() if key not in (\"created_at\", \"updated_at\", \"step\", \"source\", \"writes\", \"parents\")},\n        }\n    stored_status = record.get(\"status\", \"idle\")\n    status = _derive_thread_status(snapshot, pending_writes, fallback_status=stored_status) if checkpoint_id else stored_status\n\n    return ThreadResponse(\n        thread_id=thread_id,","sourceCodeStart":1030,"sourceCodeEnd":1066,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/threads.py#L1030-L1066","documentation":"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.","triggerScenarios":"GET a thread whose checkpoint blob fails to deserialize (corrupt row, serializer version change), or while checkpoint storage is unreachable/locked.","commonSituations":"Upgrading langgraph-checkpoint without migrating blobs; truncated/corrupt checkpoint rows after a crash; DB connectivity blips; concurrent write holding a lock on checkpoint tables.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const t = await api.get(`/api/threads/${id}`); }\ncatch (err) {\n  if (err.status === 500) { await backoff(); retryOnce(); } // transient storage blips self-heal\n  else throw err; // persistent 500 on one thread only => corrupt checkpoint, isolate it\n}","preventionTips":["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."],"tags":["threads","checkpointer","http-500","serialization","database"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}