bytedance/deer-flow · error
Failed to delete local thread data.
Error message
Failed to delete local thread data.
What it means
deleteLocalThreadData deletes client-side/local thread artifacts after the primary threads.delete call. A 404 is deliberately treated as success (idempotent delete — see the comment: the first delete already removed thread_meta so the ownership guard 404s). Any other non-ok status parses the body's detail, falling back to this literal when parsing fails.
Source
Thrown at frontend/src/core/threads/hooks.ts:3079
async function deleteLocalThreadData(threadId: string) {
const response = await fetch(
`${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}`,
{
method: "DELETE",
},
);
// A 404 means the thread is already gone — the desired end state. The prior
// `apiClient.threads.delete` call hits the same gateway handler (nginx
// rewrites /api/langgraph/threads/* to /api/threads/*) and removes the
// thread_meta row, so this second delete's ownership guard 404s. Treat it as
// success to keep the delete idempotent.
if (!response.ok && response.status !== 404) {
const error = await response
.json()
.catch(() => ({ detail: "Failed to delete local thread data." }));
throw new Error(error.detail ?? "Failed to delete local thread data.");
}
}
async function deleteThreadEverywhere(
apiClient: ThreadDeleteClient,
threadId: string,
) {
await apiClient.threads.delete(threadId);
await deleteLocalThreadData(threadId);
}
export async function findSidecarThreadIdsForParent(
apiClient: ThreadSidecarSearchClient,
parentThreadId: string,
) {
const threadIds: string[] = [];
const limit = 100;
let offset = 0;View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Check which status the local-thread-data DELETE returned (only 404 is whitelisted).
- 401/403: re-auth and retry the delete from the UI.
- 5xx: inspect gateway logs for the local data delete handler; verify the thread storage backend is reachable.
- If the endpoint is legitimately optional in your deployment, treat additional statuses explicitly rather than widening the 404 special case blindly.
Example fix
// before: only 404 tolerated
if (!response.ok && response.status !== 404) { throw new Error(...); }
// after: also tolerate auth-redirect HTML bodies for clearer errors
if (!response.ok && response.status !== 404) {
const body = await response.json().catch(() => null);
throw new Error(body?.detail ?? `Failed to delete local thread data (HTTP ${response.status}).`);
} Defensive patterns
Strategy: fallback
Validate before calling
// Before deleteThreadEverywhere, optionally check thread existence; main guard is server-side idempotency which already whitelists 404.
Try / catch
try { await deleteThreadEverywhere(apiClient, threadId); } catch (e) { if (/local thread data/i.test(e.message)) { removeThreadFromLocalCache(threadId); /* desired end state reached client-side */ } else throw e; } Prevention
- Keep the 404-as-success rule in sync with the gateway handler's ownership guard.
- Parse JSON defensively and include status in thrown errors.
- Make client cache cleanup independent of server delete success where UX allows.
When it happens
Trigger: The secondary delete endpoint returns 500 (storage failure), 401/403 (session expired between the two calls), or a non-JSON error body (proxy error page) making response.json() throw and the fallback text used.
Common situations: Deleting a thread while the gateway is restarting; race where the user's session expires mid-delete; a sandboxed/local storage backend unavailable at delete time.
Related errors
- Failed to delete file
- Failed to load thread history.
- Failed to create side conversation.
- Failed to load thread token usage.
- Failed to branch conversation.
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/685812c5981d102c.
Report an issue: GitHub.