bytedance/deer-flow · warning
Failed to fetch subtask steps: ${res.status}
Error message
Failed to fetch subtask steps: ${res.status} What it means
Thrown when a paginated GET of thread events filtered to event_types=subagent.step&task_id=... fails. The loop pages through the event stream (limit=pageSize, after_seq cursor) up to SUBTASK_STEPS_MAX_PAGES to reconstruct subtask steps client-side. Any non-2xx page aborts the whole fetch with only the numeric status in the message.
Source
Thrown at frontend/src/core/tasks/api.ts:49
threadId,
)}/runs/${encodeURIComponent(runId)}/events`;
const events: FetchedEvent[] = [];
let afterSeq: number | undefined;
for (let page = 0; page < SUBTASK_STEPS_MAX_PAGES; page++) {
const params = new URLSearchParams({
event_types: "subagent.step",
task_id: taskId,
limit: String(pageSize),
});
if (afterSeq !== undefined) {
params.set("after_seq", String(afterSeq));
}
const res = await fetch(`${base}?${params.toString()}`);
if (!res.ok) {
throw new Error(`Failed to fetch subtask steps: ${res.status}`);
}
const batch = (await res.json()) as FetchedEvent[];
events.push(...batch);
if (batch.length < pageSize) {
break;
}
const lastSeq = batch[batch.length - 1]?.seq;
if (lastSeq === undefined) {
break; // can't advance the cursor; stop rather than refetch page 0 forever
}
afterSeq = lastSeq;
}
return eventsToSteps(events, taskId);
}
View on GitHub (pinned to 1dd6ba1acb)
Solutions
- On 404/403, stop showing the subtask panel for that task instead of erroring the whole view
- Confirm the thread exists via GET /api/threads/{id} before opening subtasks
- Check the raw URL in devtools to verify event_types/task_id/after_seq query encoding
- If 500s correlate with load, retry the single failed page with the same cursor after a short delay
Example fix
// before
const res = await fetch(`${base}?${params.toString()}`);
if (!res.ok) throw new Error(`Failed to fetch subtask steps: ${res.status}`);
// after
const res = await fetch(`${base}?${params.toString()}`);
if (!res.ok) {
if (res.status === 404 || res.status === 403) return []; // task gone / no access
throw new Error(`Failed to fetch subtask steps: ${res.status}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
function validEventQuery(taskId: string, afterSeq?: number): boolean {
return taskId.length > 0 && (afterSeq === undefined || Number.isSafeInteger(afterSeq) && afterSeq >= 0);
} Type guard
export function isSubtaskStepsError(e: unknown): e is Error {
return e instanceof Error && e.message.startsWith('Failed to fetch subtask steps:');
} Try / catch
try {
return await fetchSubtaskSteps(taskId);
} catch (e) {
if (isSubtaskStepsError(e)) {
const status = Number(e.message.split(': ')[1]);
if (status === 404 || status === 403) return []; // gone / no access
return retryOnce(() => fetchSubtaskSteps(taskId));
}
throw e;
} Prevention
- Treat 404/403 as empty result, not an error
- Verify thread existence before opening the subtask panel
- Keep pagination cursor (after_seq) monotonic to avoid refetch loops
When it happens
Trigger: Fetching steps for a deleted or expired thread (404); unauthenticated access to another user's thread events (403); events endpoint returning 500 during heavy event writes; after_seq pointing past retention boundaries.
Common situations: Subtask panel loading an old thread whose events were pruned by retention; session expiry mid-pagination; backend version where the event-types filter param changed.
Related errors
- Failed to load MCP configuration
- Failed to update MCP configuration
- Failed to update MCP server
- HTTP ${response.status}: ${response.statusText}
- Failed to load artifact: ${response.status}
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/82d98642f6c9174d.
Report an issue: GitHub.