{"record":{"id":"528f3fddf40cc1a8","repo":"payloadcms/payload","slug":"uploaded-file-size-does-not-match-the-expected-siz","errorCode":null,"errorMessage":"Uploaded file size does not match the expected size.","messagePattern":"Uploaded file size does not match the expected size\\.","errorType":"exception","errorClass":"APIError","httpStatus":400,"severity":"error","filePath":"packages/payload/src/uploads/stagedUpload.ts","lineNumber":103,"sourceCode":"\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({\n      collection,\n      file: {\n        name: upload.filename,\n        data: Buffer.alloc(0),\n        mimetype: upload.mimeType,\n        size: upload.filesize,\n        tempFilePath: temporaryPath,\n      },\n      req,\n    })\n","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/payloadcms/payload/blob/00c58b35c0ed348ddc22daabf467b139727214fd/packages/payload/src/uploads/stagedUpload.ts#L85-L121","documentation":"Thrown after the PUT body stream for a staged upload finishes: the total bytes received do not equal the `filesize` declared when the staged upload instructions were generated. Payload uses the declared size to pre-validate and atomically rename the temp file, so a mismatch means the upload cannot be finalized.","triggerScenarios":"A client sends a PUT to the `/upload-instructions/:uploadId` URL with a body whose total byte count is less than (or, having passed the larger-than check, somehow not equal to) the `filesize` encoded in the signed uploadId JWT. Typical causes: the client aborted early, a proxy truncated the body, or `Content-Length` / stream length disagrees with the value passed to `generateStagedUploadInstructions`.","commonSituations":"Client-side progress upload that stops sending chunks before completion; a reverse proxy or CDN that buffers or strips part of the body; the `filesize` passed at instruction-generation time was computed incorrectly (e.g. using a `File` object whose size changed, or a stat read before the file finished writing).","solutions":["Verify the `filesize` value passed to `generateStagedUploadInstructions` matches the actual byte length of the blob you PUT.","Ensure the HTTP client sends the entire body without early abort -- check for network interruptions or client-side timeouts.","Confirm no intermediary (nginx client_max_body_size, CDN, or compression) alters the body length.","If resuming, request fresh upload instructions for the correct file size rather than reusing an old uploadId."],"exampleFix":"// before\nconst instructions = await generateStagedUploadInstructions({\n  filesize: file.size, // stale or wrong value\n  ...\n})\nawait fetch(instructions.request.url, { method: 'PUT', body: partialBlob })\n\n// after\nconst buf = await file.arrayBuffer()\nconst instructions = await generateStagedUploadInstructions({\n  filesize: buf.byteLength, // exact byte count\n  ...\n})\nawait fetch(instructions.request.url, { method: 'PUT', body: buf })","handlingStrategy":"validation","validationCode":"// Before PUT, confirm the blob byte length matches the declared size\nconst buf = await file.arrayBuffer()\nif (buf.byteLength !== declaredFilesize) {\n  throw new Error(`Size mismatch: blob is ${buf.byteLength}, expected ${declaredFilesize}`)\n}\nawait fetch(uploadUrl, { method: 'PUT', body: buf, headers: { 'Content-Length': String(buf.byteLength) } })","typeGuard":null,"tryCatchPattern":"// Wrap the PUT; on failure, re-stage instead of retrying with stale uploadId\ntry {\n  await putStagedBytes(uploadId, buffer)\n} catch (e) {\n  if (e instanceof APIError && e.message.includes('does not match')) {\n    const fresh = await generateStagedUploadInstructions({ filesize: buffer.byteLength, ... })\n    await putStagedBytes(fresh.uploadId, buffer)\n  } else throw e\n}","preventionTips":["Always compute filesize from the exact buffer/arrayBuffer you will PUT, not from a File metadata field that may be stale.","Set Content-Length explicitly on the PUT request.","Treat upload instructions as single-use; regenerate on any failure."],"tags":["upload","staged-upload","size-mismatch","validation"],"backgroundTag":null,"analyzedSha":"00c58b35c0ed348ddc22daabf467b139727214fd","analyzedAt":"2026-08-12T20:45:03.758Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}