{"record":{"id":"2d75598227f8d88e","repo":"bytedance/deer-flow","slug":"thread-is-not-ready-for-file-upload","errorCode":null,"errorMessage":"Thread is not ready for file upload.","messagePattern":"Thread is not ready for file upload\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/core/threads/hooks.ts","lineNumber":2145,"sourceCode":"          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,\n                  status: \"uploaded\" as const,\n                }),\n              );\n              setOptimisticMessages((messages) => {\n                if (messages.length > 1 && messages[0]) {\n                  const humanMessage: Message = messages[0];","sourceCodeStart":2127,"sourceCodeEnd":2163,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/frontend/src/core/threads/hooks.ts#L2127-L2163","documentation":"Guard inside the send path: attachments are converted to File objects first, and only then is threadId checked. If the thread id is falsy (empty string, null, undefined) at that point, the upload cannot be addressed to /api/threads/{id}/uploads, so it throws instead of uploading to nowhere.","triggerScenarios":"User sends a message with files before the thread has been created/server-assigned — e.g. an optimistic send on a brand-new thread whose id promise has not resolved, or a send triggered from a stale component after thread deletion cleared the id.","commonSituations":"Race on first message in a new thread (title/id creation still in flight), a component holding a stale threadId after switching threads mid-upload, or a demo/static thread that has no server id.","solutions":["Await thread id creation before enabling send-with-attachments (disable the attach/send button until threadId is truthy).","If sending on a new thread, create the thread first, then run the upload+send sequence with the resolved id.","Check for a deleted/switched thread: cancel the in-flight send when the active thread id changes."],"exampleFix":"// before: send proceeds with a possibly-empty id\nawait sendMessage({ ...message, files });\n\n// after: gate the send on the resolved thread id\nconst threadId = await ensureThreadCreated();\nif (!threadId) { throw new Error('Thread is not ready for file upload.'); }\nawait sendMessage({ ...message, files });","handlingStrategy":"validation","validationCode":"if (!threadId) { /* block send-with-attachments in the UI: disable attach button or create thread first */ }","typeGuard":"function isReadyForUpload(threadId: unknown): threadId is string {\n  return typeof threadId === \"string\" && threadId.length > 0;\n}","tryCatchPattern":"catch (e) { if (e.message === \"Thread is not ready for file upload.\") { await ensureThreadCreated(); /* retry once */ } else throw e; }","preventionTips":["Create the thread before enabling attachment send on new threads.","Cancel in-flight sends when the active thread id changes.","Type the send path so threadId is non-null by construction (create-then-send sequence)."],"tags":["attachments","upload","race-condition","threads","frontend"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}