{"record":{"id":"3fe4b6f3f3e35738","repo":"bytedance/deer-flow","slug":"failed-to-prepare-failedconversions-attachment","errorCode":null,"errorMessage":"Failed to prepare ${failedConversions} attachment(s) for upload. Please retry.","messagePattern":"Failed to prepare (.+?) attachment\\(s\\) for upload\\. Please retry\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/core/threads/hooks.ts","lineNumber":2139,"sourceCode":"      let uploadedFileInfo: UploadedFileInfo[] = [];\n\n      try {\n        // Upload files first if any\n        if (message.files && message.files.length > 0) {\n          setIsUploading(true);\n          try {\n            const filePromises = message.files.map((fileUIPart) =>\n              promptInputFilePartToFile(fileUIPart),\n            );\n\n            const conversionResults = await Promise.all(filePromises);\n            const files = conversionResults.filter(\n              (file): file is File => file !== null,\n            );\n            const failedConversions = conversionResults.length - files.length;\n\n            if (failedConversions > 0) {\n              throw new Error(\n                `Failed to prepare ${failedConversions} attachment(s) for upload. Please retry.`,\n              );\n            }\n\n            if (!threadId) {\n              throw new Error(\"Thread is not ready for file upload.\");\n            }\n\n            if (files.length > 0) {\n              const uploadResponse = await uploadFiles(threadId, files);\n              uploadedFileInfo = uploadResponse.files;\n\n              // Update optimistic human message with uploaded status + paths\n              const uploadedFiles: FileInMessage[] = uploadedFileInfo.map(\n                (info) => ({\n                  filename: info.filename,\n                  size: info.size,\n                  path: info.virtual_path,","sourceCodeStart":2121,"sourceCodeEnd":2157,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/frontend/src/core/threads/hooks.ts#L2121-L2157","documentation":"Thrown during message send when converting the prompt input's file parts to File objects via promptInputFilePartToFile. Each conversion returning null (unsupported or unreadable part) is counted, and any nonzero count aborts the send with this error before upload, asking the user to retry.","triggerScenarios":"message.files contains a file UI part whose format promptInputFilePartToFile does not handle (returns null) — e.g. a part with no underlying blob/URL data, an inline base64 part with a corrupt data URL, or a part kind introduced by a newer editor that this converter predates.","commonSituations":"Pasting an image whose transfer failed in the browser, a dragged file part constructed by an outdated extension of the prompt editor, or version skew between the chat input component and the converter in hooks.ts.","solutions":["Reproduce with the same attachment and console.log the failing file part to see which kind/shape returns null.","Re-attach the file (paste/drop again) — a corrupted in-memory part is often transient; hence the 'Please retry' wording.","Extend promptInputFilePartToFile to handle the part kind, or normalize the editor to only emit supported kinds.","Update the frontend to a consistent version so the input component and converter agree on part shapes."],"exampleFix":"// before: unsupported part silently becomes null and aborts the whole send\nconst file = promptInputFilePartToFile(part); // null for {type: 'file', kind: 'reference'}\n\n// after: convert reference parts via their stored path\nfunction promptInputFilePartToFile(part) {\n  if (part.kind === 'reference' && part.path) { return referenceToFile(part.path); }\n  return legacyConverter(part);\n}","handlingStrategy":"validation","validationCode":"const supported = message.files.every((p) => isConvertibleFilePart(p));\nif (!supported) { /* disable send, show per-file error instead of aborting whole message */ }","typeGuard":"function isConvertibleFilePart(part: unknown): boolean {\n  if (typeof part !== \"object\" || part === null) return false;\n  const kind = Reflect.get(part, \"kind\") ?? Reflect.get(part, \"media_type\");\n  return Boolean(kind); // mirror the branches promptInputFilePartToFile handles","tryCatchPattern":"try { files = await Promise.all(message.files.map(promptInputFilePartToFile)); } catch (e) { toast(`Attachment issue: ${e.message}`); return; // keep the draft intact for the user to fix }","preventionTips":["Validate file parts at attach time (drop/paste), not at send time, so users learn immediately.","Keep the editor's emitted part kinds and the converter's supported kinds in one shared type.","Surface which attachment failed rather than only a count."],"tags":["attachments","upload","frontend","validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}