bytedance/deer-flow · error

Upload failed

Error message

Upload failed

What it means

uploadFiles POSTs multipart form data ('files' entries) to /api/threads/{threadId}/uploads and throws on any non-ok response. readErrorDetail prefers the gateway's error detail (e.g. file too large, disallowed type) and falls back to 'Upload failed' when the body has none.

Solutions

  1. Fetch and enforce the limits first via getUploadLimits(threadId) — validate size/type client-side before the POST.
  2. Match the error detail the gateway returned (it names the actual limit or reason) in the Network tab.
  3. 401: re-authenticate; 404: verify the thread still exists before retrying.
  4. 5xx: check gateway logs for the uploads handler and storage mount health.

Example fix

// before: fire and hope
const res = await uploadFiles(threadId, files);

// after: pre-validate against gateway limits
const limits = await getUploadLimits(threadId);
const oversized = files.filter((f) => f.size > limits.max_file_size);
if (oversized.length) { throw new Error(`File exceeds ${limits.max_file_size} bytes: ${oversized[0].name}`); }
const res = await uploadFiles(threadId, files);
Defensive patterns

Strategy: validation

Validate before calling

const limits = await getUploadLimits(threadId);
const tooBig = files.filter((f) => f.size > limits.max_file_size);
const tooMany = files.length > limits.max_files;
if (tooBig.length || tooMany) { /* block and explain before POST */ }

Type guard

function isWithinLimits(files: File[], limits: UploadLimits): boolean {
  return files.length <= limits.max_files && files.every((f) => f.size <= limits.max_file_size);
}

Try / catch

try { const res = await uploadFiles(threadId, files); } catch (e) { toast(e.message || 'Upload failed'); keepFilesSelectedForRetry(); }

Prevention

When it happens

Trigger: Uploading a file exceeding the gateway's size limit, a disallowed MIME type/extension, uploading to a deleted/nonexistent thread id (404), expired session (401), or gateway storage backend failure (5xx).

Common situations: Users attaching large videos/archives past the configured limit, uploading immediately after session expiry, thread deleted in another tab while the upload dialog was open.

Related errors


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

Appendix: source

Thrown at frontend/src/core/uploads/api.ts:70

  threadId: string,
  files: File[],
): Promise<UploadResponse> {
  const formData = new FormData();

  files.forEach((file) => {
    formData.append("files", file);
  });

  const response = await fetch(
    `${getBackendBaseURL()}/api/threads/${threadId}/uploads`,
    {
      method: "POST",
      body: formData,
    },
  );

  if (!response.ok) {
    throw new Error(await readErrorDetail(response, "Upload failed"));
  }

  return response.json();
}

/**
 * Load the upload limits enforced by the gateway for a thread
 */
export async function getUploadLimits(threadId: string): Promise<UploadLimits> {
  const response = await fetch(
    `${getBackendBaseURL()}/api/threads/${threadId}/uploads/limits`,
  );

  if (!response.ok) {
    throw new Error(
      await readErrorDetail(response, "Failed to load upload limits"),
    );
  }

View on GitHub (pinned to 1dd6ba1acb)