{"record":{"id":"7124c4502de4a0ea","repo":"payloadcms/payload","slug":"uploaded-file-is-larger-than-expected","errorCode":null,"errorMessage":"Uploaded file is larger than expected.","messagePattern":"Uploaded file is larger than expected\\.","errorType":"http","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/payload/src/uploads/stagedUpload.ts","lineNumber":92,"sourceCode":"  const uploadPath = path.join(directory, upload.id)\n  const temporaryPath = `${uploadPath}.${randomUUID()}.part`\n  const file = await fs.open(temporaryPath, 'wx')\n  let uploadedSize = 0\n\n  try {\n    try {\n      const reader = req.body?.getReader()\n\n      while (reader) {\n        const { done, value } = await reader.read()\n\n        if (done) {\n          break\n        }\n\n        uploadedSize += value.byteLength\n        if (uploadedSize > upload.filesize) {\n          throw new APIError('Uploaded file is larger than expected.', 400)\n        }\n\n        let offset = 0\n        while (offset < value.byteLength) {\n          const { bytesWritten } = await file.write(value, offset)\n          offset += bytesWritten\n        }\n      }\n\n      if (uploadedSize !== upload.filesize) {\n        throw new APIError('Uploaded file size does not match the expected size.', 400)\n      }\n    } finally {\n      await file.close()\n    }\n\n    const collection = req.payload.collections[upload.collectionSlug]!.config\n    await checkFileRestrictions({","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/uploads/stagedUpload.ts#L74-L110","documentation":"The staged `PUT /upload-instructions/:uploadId` streams the request body to disk, accumulating `uploadedSize` per chunk. If at any point `uploadedSize` exceeds the `filesize` declared when the upload ID was minted (signed JWT containing `filesize`), Payload throws `APIError` HTTP 400 `Uploaded file is larger than expected.` The partial `.part` file is cleaned up in the surrounding `catch`.","triggerScenarios":"`PUT /api/upload-instructions/:uploadId` where the total streamed byte count surpasses the `filesize` baked into the signed uploadId token. The client sent more bytes than it declared in the prior `POST /upload-instructions`.","commonSituations":"The client computed `filesize` from a different (smaller) artifact than the one it actually streams (e.g. pre- vs post-compression, or a different file). A retry/resume appended to an existing stream. A proxy or middleware added bytes/chunked encoding unexpectedly. The client reused a stale uploadId for a larger file. Concurrency: two PUTs to the same id.","solutions":["Request fresh upload instructions (`POST /upload-instructions`) with the exact byte length of the file you will stream, then PUT exactly that many bytes.","Compute `filesize` from the actual `File`/`Blob`/buffer you upload (`file.size`), not a metadata estimate.","Do not reuse an uploadId across different files or after the file changes.","Ensure the HTTP client doesn't transform the body (extra framing, appended trailers).","If resumable uploads are needed, re-mint the uploadId rather than appending to a used one."],"exampleFix":"// before — filesize mismatch\nconst { file } = await getUploadInstructions({\n  collectionSlug: 'media', filename: 'a.png', filesize: 1024, mimeType: 'image/png', req, // wrong size\n})\nawait fetch(file.request.url, { method: 'PUT', body: realBlob /* 2048 bytes */ })\n\n// after — declare the true size\nconst realSize = realBlob.size\nconst { file } = await getUploadInstructions({\n  collectionSlug: 'media', filename: 'a.png', filesize: realSize, mimeType: 'image/png', req,\n})\nawait fetch(file.request.url, { method: 'PUT', body: realBlob })","handlingStrategy":"validation","validationCode":"const realSize = file.size // or Buffer.byteLength(buffer)\nconst { file: instr } = await getUploadInstructions({\n  collectionSlug: 'media', filename, filesize: Math.trunc(realSize), mimeType, req,\n})\nif (realSize !== Math.trunc(realSize)) {\n  throw new Error('filesize must be a non-negative safe integer matching the stream length')\n}","typeGuard":"function sizesMatch(declared: number, actual: number): boolean {\n  return Number.isSafeInteger(declared) && declared >= 0 && actual <= declared\n}\nif (!sizesMatch(instr.size, blob.size)) {\n  // re-request instructions with the correct size\n}","tryCatchPattern":"try {\n  const res = await fetch(instr.request.url, { method: 'PUT', body: blob })\n  if (res.status === 400) {\n    const { message } = await res.json().catch(() => ({}))\n    if (/larger than expected/i.test(message ?? '')) {\n      // re-mint instructions with blob.size and PUT again\n    }\n  }\n} catch (err) { /* network */ }","preventionTips":["Derive `filesize` from the exact bytes you will stream (`file.size`).","Re-request instructions whenever the file changes; never reuse an uploadId across files.","Avoid middleware that mutates the request body length."],"tags":["upload","staged-upload","file-size","validation"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}