{"record":{"id":"ca5ed7ae3860127e","repo":"bytedance/deer-flow","slug":"failed-to-branch-conversation","errorCode":null,"errorMessage":"Failed to branch conversation.","messagePattern":"Failed to branch conversation\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/core/threads/api.ts","lineNumber":105,"sourceCode":"  input: BranchThreadFromTurnInput,\n): Promise<ThreadBranchResponse> {\n  const response = await fetchWithAuth(\n    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/branches`,\n    {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        message_id: input.messageId,\n        message_ids: input.messageIds ?? [input.messageId],\n        ...(input.title ? { title: input.title } : {}),\n      }),\n    },\n  );\n\n  if (!response.ok) {\n    throw new Error(\n      await readThreadAPIError(response, \"Failed to branch conversation.\"),\n    );\n  }\n\n  return (await response.json()) as ThreadBranchResponse;\n}\n\nexport async function patchThreadMetadata(\n  threadId: string,\n  metadata: ThreadMetadataPatch,\n): Promise<ThreadMetadataPatchResponse> {\n  const response = await fetchWithAuth(\n    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}`,\n    {\n      method: \"PATCH\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/frontend/src/core/threads/api.ts#L87-L123","documentation":"Thrown when POST /api/threads/{id}/branches (branch a conversation from one or more turns) returns non-2xx. readThreadAPIError layers the backend detail over the fallback text. The backend validates the referenced message_ids and thread state: 404 unknown thread or message, 409 branching a thread mid-run, 422 empty/malformed message id list.","triggerScenarios":"Branching from a message that was deleted or belongs to another thread; branching while an agent run is active on the source thread; sending message_ids that don't share a consistent seq ordering.","commonSituations":"User clicks 'branch from here' on an old message after history was compacted (message ids no longer resolvable); double-click creating two branch requests; branching from a turn still streaming.","solutions":["Read the wrapped detail message — it names whether thread or message was not found","Disable the branch action while a run is streaming; retry after completion","Refresh the thread's message list before branching so ids are current","Guard against double-submit by disabling the button during the request"],"exampleFix":"// before\nconst branch = await branchThreadFromTurn(threadId, {messageId});\n\n// after\nconst branch = await branchThreadFromTurn(threadId, {messageId}).catch((e) => {\n  if (/not found/i.test(e.message)) {\n    await refreshMessages(threadId);\n    return null; // message gone; user re-picks a turn\n  }\n  throw e;\n});","handlingStrategy":"try-catch","validationCode":"function branchInputValid(input: BranchThreadFromTurnInput, validIds: Set<string>): boolean {\n  return validIds.has(input.messageId) && (input.messageIds ?? [input.messageId]).every((id) => validIds.has(id));\n}","typeGuard":"export function isThreadBranchError(e: unknown): e is Error {\n  return e instanceof Error && e.message.includes('Failed to branch conversation.');\n}","tryCatchPattern":"try {\n  return await branchThreadFromTurn(threadId, input);\n} catch (e) {\n  if (isThreadBranchError(e) && /not found/i.test(e.message)) {\n    await refreshThread(threadId);\n    return null; // caller re-selects a turn\n  }\n  throw e;\n}","preventionTips":["Disable branch actions while a run is streaming","Validate message ids against the loaded page before submitting","Prevent double-submit on the branch button"],"tags":["threads","branching","http","frontend"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}