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

  1. Check the Gateway log traceback for the underlying SQL error.
  2. Verify DB health and apply pending migrations.
  3. Keep PATCH metadata small and flat; split large updates into several PATCHes.
  4. 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

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


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