{"record":{"id":"81c3c0f51977bbef","repo":"payloadcms/payload","slug":"staged-upload-is-incomplete","errorCode":null,"errorMessage":"Staged upload is incomplete.","messagePattern":"Staged upload is incomplete\\.","errorType":"exception","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/payload/src/uploads/stagedUpload.ts","lineNumber":183,"sourceCode":"  const upload = await verifyUploadID(req, uploadId)\n\n  if (upload.collectionSlug !== collectionSlug || upload.user !== getUser(req)) {\n    throw new Forbidden(req.t)\n  }\n\n  const directory = await getUploadDirectory(req, upload.collectionSlug)\n  const tempFilePath = path.join(directory, upload.id)\n  let data: Buffer\n\n  try {\n    data = await fs.readFile(tempFilePath)\n  } catch {\n    throw new APIError('Staged upload was not found.', 400)\n  }\n\n  if (data.length !== upload.filesize) {\n    await fs.rm(tempFilePath, { force: true })\n    throw new APIError('Staged upload is incomplete.', 400)\n  }\n\n  await fs.rm(tempFilePath, { force: true })\n\n  return {\n    name: upload.filename,\n    data,\n    mimetype: upload.mimeType,\n    size: data.length,\n  }\n}\n\n/**\n * Staged uploads hold a file temporarily until a document create or update uses it. The signed\n * upload ID remembers which file and user the upload belongs to.\n */\nconst tokenExpiration = 60 * 60\n","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/uploads/stagedUpload.ts#L165-L201","documentation":"Thrown by `getStagedFile` when the temp file exists on disk but its byte length does not equal `upload.filesize`. This means the PUT that was supposed to deliver the bytes either did not complete or wrote a partial file.","triggerScenarios":"The PUT to `/upload-instructions/:uploadId` was interrupted mid-stream (client disconnect, network drop) so the `.part` file was renamed or a prior partial write persisted; or a crash happened between writing and the final size check.","commonSituations":"Client disconnected or timed out during the chunked PUT; a proxy closed the connection early; the server crashed mid-upload and on restart the temp file is stale; clock or filesystem corruption caused a truncated write.","solutions":["Re-stage the file: generate new upload instructions, re-PUT the full byte stream, then consume.","Check client-side for upload aborts -- increase timeouts and add retry on the PUT step specifically.","Verify no intermediary truncates the body (nginx proxy_read_timeout, CDN body limits).","Ensure the temp directory has enough disk space for the full file."],"exampleFix":"// before\nconst instr = await generateStagedUploadInstructions({ filesize: file.size, ... })\nawait fetch(instr.request.url, { method: 'PUT', body: streamThatMayAbort })\nawait createWithUpload(instr) // partial file on disk -> incomplete\n\n// after\nconst instr = await generateStagedUploadInstructions({ filesize: file.size, ... })\nconst res = await fetch(instr.request.url, { method: 'PUT', body: fullBuffer })\nif (!res.ok) throw new Error('PUT failed, re-stage before retrying')\nawait createWithUpload(instr)","handlingStrategy":"try-catch","validationCode":"// After PUT, verify the response is 204 before consuming\nconst res = await fetch(uploadUrl, { method: 'PUT', body: buf })\nif (res.status !== 204) {\n  throw new Error('PUT did not complete successfully -- do not consume')\n}","typeGuard":null,"tryCatchPattern":"try {\n  await payload.create({ collection, data })\n} catch (e) {\n  if (e instanceof APIError && e.message === 'Staged upload is incomplete.') {\n    // re-stage and re-upload the full file\n  } else throw e\n}","preventionTips":["Verify the PUT returns 204 before proceeding to create/update.","Increase client and proxy timeouts for large uploads.","Ensure sufficient disk space on the temp directory."],"tags":["upload","staged-upload","incomplete","partial-write"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}