bytedance/deer-flow · error
Failed to branch conversation.
Error message
Failed to branch conversation.
What it means
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.
Source
Thrown at frontend/src/core/threads/api.ts:105
input: BranchThreadFromTurnInput,
): Promise<ThreadBranchResponse> {
const response = await fetchWithAuth(
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/branches`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message_id: input.messageId,
message_ids: input.messageIds ?? [input.messageId],
...(input.title ? { title: input.title } : {}),
}),
},
);
if (!response.ok) {
throw new Error(
await readThreadAPIError(response, "Failed to branch conversation."),
);
}
return (await response.json()) as ThreadBranchResponse;
}
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",
},View on GitHub (pinned to 1dd6ba1acb)
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
Example fix
// before
const branch = await branchThreadFromTurn(threadId, {messageId});
// after
const branch = await branchThreadFromTurn(threadId, {messageId}).catch((e) => {
if (/not found/i.test(e.message)) {
await refreshMessages(threadId);
return null; // message gone; user re-picks a turn
}
throw e;
}); Defensive patterns
Strategy: try-catch
Validate before calling
function branchInputValid(input: BranchThreadFromTurnInput, validIds: Set<string>): boolean {
return validIds.has(input.messageId) && (input.messageIds ?? [input.messageId]).every((id) => validIds.has(id));
} Type guard
export function isThreadBranchError(e: unknown): e is Error {
return e instanceof Error && e.message.includes('Failed to branch conversation.');
} Try / catch
try {
return await branchThreadFromTurn(threadId, input);
} catch (e) {
if (isThreadBranchError(e) && /not found/i.test(e.message)) {
await refreshThread(threadId);
return null; // caller re-selects a turn
}
throw e;
} Prevention
- Disable branch actions while a run is streaming
- Validate message ids against the loaded page before submitting
- Prevent double-submit on the branch button
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to create side conversation.
- Failed to load thread token usage.
- Failed to update conversation.
- Failed to compact context.
- Failed to load MCP configuration
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/ca5ed7ae3860127e.
Report an issue: GitHub.