bytedance/deer-flow · error

Failed to update conversation.

Error message

Failed to update conversation.

What it means

Thrown when PATCH /api/threads/{id} with {metadata} returns non-2xx. This is the rename/tag/update-conversation call; readThreadAPIError surfaces the backend reason. 404 means the thread no longer exists, 409 usually a conflicting concurrent patch (metadata merge failure), 422 metadata that fails the backend's schema/size limits.

Source

Thrown at frontend/src/core/threads/api.ts:129

}

export async function patchThreadMetadata(
  threadId: string,
  metadata: ThreadMetadataPatch,
): Promise<ThreadMetadataPatchResponse> {
  const response = await fetchWithAuth(
    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}`,
    {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ metadata }),
    },
  );

  if (!response.ok) {
    throw new Error(
      await readThreadAPIError(response, "Failed to update conversation."),
    );
  }

  return (await response.json()) as ThreadMetadataPatchResponse;
}

export async function compactThreadContext(
  threadId: string,
  options: CompactThreadContextOptions = {},
): Promise<ThreadCompactResponse> {
  const response = await fetchWithAuth(
    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/compact`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. On 404, drop the local thread from the list — it was deleted server-side
  2. On 409, re-fetch the thread, re-apply the user's edit onto fresh metadata, patch again
  3. Keep metadata values within backend limits (truncate long titles client-side)
  4. Debounce rapid successive edits into one PATCH

Example fix

// before
await patchThreadMetadata(threadId, {title});

// after
try {
  await patchThreadMetadata(threadId, {title});
} catch (e) {
  if (/409|conflict/i.test(e.message)) {
    await reloadThread(threadId);
    await patchThreadMetadata(threadId, {title}); // one re-apply
    return;
  }
  throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

function metadataPatchOk(metadata: ThreadMetadataPatch): boolean {
  const s = JSON.stringify(metadata);
  return s.length <= 4096 && Object.values(metadata).every((v) => v !== undefined);
}

Type guard

export function isThreadPatchError(e: unknown): e is Error {
  return e instanceof Error && e.message.includes('Failed to update conversation.');
}

Try / catch

try {
  return await patchThreadMetadata(threadId, metadata);
} catch (e) {
  if (isThreadPatchError(e) && /conflict|409/i.test(e.message)) {
    await reloadThread(threadId);
    return patchThreadMetadata(threadId, metadata); // one optimistic retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Renaming a thread deleted in another tab (404); two rapid metadata patches racing (409); metadata values exceeding size limits or containing disallowed keys (422); session expired (401).

Common situations: Editing title/tags while an agent run also updates thread metadata; very long pasted titles; multi-tab usage.

Related errors


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