{"record":{"id":"d56705b6a7c07c58","repo":"fullstackhero/dotnet-starter-kit","slug":"uploaded-size-head-sizebytes-exceeds-declared-asset","errorCode":null,"errorMessage":"uploaded size ({head.SizeBytes}) exceeds declared ({asset.SizeBytes})","messagePattern":"uploaded size \\((.+?)\\) exceeds declared \\((.+?)\\)","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Files/Modules.Files/Features/v1/FinalizeUpload/FinalizeUploadCommandHandler.cs","lineNumber":60,"sourceCode":"        {\n            throw new ForbiddenException(\"not your pending file\");\n        }\n        if (asset.Status != FileAssetStatus.PendingUpload)\n        {\n            throw new CustomException(\"file already finalized\", (IEnumerable<string>?)null, HttpStatusCode.Conflict);\n        }\n\n        var head = await storage.HeadObjectAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false)\n            ?? throw new CustomException(\"upload not received\", (IEnumerable<string>?)null, HttpStatusCode.Conflict);\n\n        // Allow declared+1% slack (S3 may differ slightly on multipart). Reject larger sizes.\n        var maxAllowed = asset.SizeBytes + Math.Max(1024L, asset.SizeBytes / 100);\n        if (head.SizeBytes > maxAllowed)\n        {\n            await storage.RemoveAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);\n            db.FileAssets.Remove(asset);\n            await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n            throw new CustomException(\n                $\"uploaded size ({head.SizeBytes}) exceeds declared ({asset.SizeBytes})\",\n                (IEnumerable<string>?)null,\n                HttpStatusCode.BadRequest);\n        }\n\n        if (!string.Equals(head.ContentType, asset.ContentType, StringComparison.OrdinalIgnoreCase))\n        {\n            await storage.RemoveAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);\n            db.FileAssets.Remove(asset);\n            await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);\n            throw new CustomException(\n                \"uploaded content-type mismatch\",\n                (IEnumerable<string>?)null,\n                HttpStatusCode.BadRequest);\n        }\n\n        var scanResult = await scanner.ScanAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);\n        asset.MarkAvailable(head.SizeBytes, scanResult);","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Files/Modules.Files/Features/v1/FinalizeUpload/FinalizeUploadCommandHandler.cs#L42-L78","documentation":"After finalize confirms the object exists, the handler compares the actual stored object size (head.SizeBytes) against the declared asset.SizeBytes plus a slack of max(1KB, 1%). If the uploaded object is larger than that allowance, the handler deletes the uploaded blob, removes the FileAsset row, and throws CustomException with HTTP 400 BadRequest.","triggerScenarios":"The client uploaded more bytes than declared at upload-request time — e.g. the file changed on disk between size declaration and upload, the uploader appended extra bytes/boundary data, or a different file was streamed to the presigned URL.","commonSituations":"Reading a growing log/temp file while uploading; a multipart wrapper adding bytes in a hand-rolled uploader; declaring size from a stale file stat then uploading the rewritten file; user replacing the selected file after the upload request was created.","solutions":["Re-declare the upload with the correct SizeBytes and re-upload the exact file that matches it","Hash/compare the file client-side after declaring size and abort if it changed","Verify the uploader writes only the file bytes (no extra framing) to the presigned URL","If your storage legitimately exceeds declared size (multipart overhead), keep sizes within the declared +1%/1KB slack"],"exampleFix":"// before\nvar size = file.Length; // captured earlier\nawait upload(url, file); // file has since grown\n// after\nawait using var fs = File.OpenRead(path);\nif (fs.Length != declaredSize) { /* re-request upload with new size */ }\nawait upload(url, fs);","handlingStrategy":"validation","validationCode":"if (file.size !== declaredSizeBytes) {\n  throw new Error(`file is ${file.size} bytes but ${declaredSizeBytes} were declared`);\n}","typeGuard":null,"tryCatchPattern":"try { await api.finalizeUpload(assetId); }\ncatch (e) { if (e.status === 400 && /exceeds declared/.test(e.message)) { await requestNewUpload(file); } else throw e; }","preventionTips":["Re-stat the file immediately before upload and compare to the declared size","Upload a snapshot/copy, not a file that may change mid-transfer","Send only raw file bytes to the presigned URL (no extra framing)"],"tags":["upload","file-size","validation","files"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}