bytedance/deer-flow · error · HTTPException
Failed to update thread
Error message
Failed to update thread
What it means
500 from PATCH /threads/{thread_id} when thread_store.update_metadata(thread_id, body.metadata, touch=touch) raises — the merge write to the thread store failed after the record was read. Logged as 'Failed to patch thread %s' with the full traceback.
Source
Thrown at backend/app/gateway/routers/threads.py:1009
async def patch_thread(thread_id: ThreadId, body: ThreadPatchRequest, request: Request) -> ThreadResponse:
"""Merge metadata into a thread record."""
from app.gateway.deps import get_thread_store
thread_store = get_thread_store(request)
record = await thread_store.get(thread_id)
if record is None:
raise HTTPException(status_code=404, detail=f"Thread {thread_id} not found")
# ``body.metadata`` already stripped by ``ThreadPatchRequest._strip_reserved``.
# Pin/unpin is not conversation activity, so it must not bump ``updated_at``.
# Other metadata PATCH callers keep the public endpoint's existing recency
# contract unless they get their own explicit no-touch API surface.
touch = not _is_pin_metadata_patch(body.metadata)
try:
await thread_store.update_metadata(thread_id, body.metadata, touch=touch)
except Exception:
logger.exception("Failed to patch thread %s", sanitize_log_param(thread_id))
raise HTTPException(status_code=500, detail="Failed to update thread")
# Re-read to get the merged metadata and the store's timestamp decision.
record = await thread_store.get(thread_id) or record
return ThreadResponse(
thread_id=thread_id,
status=record.get("status", "idle"),
created_at=coerce_iso(record.get("created_at", "")),
updated_at=coerce_iso(record.get("updated_at", "")),
metadata=record.get("metadata", {}),
)
@router.get("/{thread_id}", response_model=ThreadResponse)
@require_permission("threads", "read", owner_check=True)
async def get_thread(thread_id: ThreadId, request: Request) -> ThreadResponse:
"""Get thread info from metadata plus the graph's materialized state."""
from app.gateway.deps import get_thread_store
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Check the Gateway log traceback for the underlying SQL error.
- Verify DB health and apply pending migrations.
- Keep PATCH metadata small and flat; split large updates into several PATCHes.
- Retry once the concurrent writer or lock holder finishes.
Defensive patterns
Strategy: retry
Try / catch
try { await api.patch(`/api/threads/${id}`, {metadata}); }
catch (err) { if (err.status === 500) { await backoff(); retryOnce(); } else throw err; } Prevention
- Keep PATCH bodies small; split large metadata updates.
- Serialize concurrent PATCHes to the same thread client-side to avoid row-lock contention.
When it happens
Trigger: PATCH while the SQL store fails on UPDATE: connection loss, lock timeout from a concurrent writer, constraint violation from the merged metadata (e.g. oversized value), or schema drift.
Common situations: Database outage or failover mid-request; two clients PATCHing the same thread with row-lock contention; metadata values exceeding column size after merge; migrations not applied.
Related errors
- Failed to create thread
- Failed to create thread checkpoint
- Failed to create branch
- Failed to get thread
- Failed to update conversation.
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/d51055ba41ebfc9e.
Report an issue: GitHub.