fullstackhero/dotnet-starter-kit · error · CustomException
uploaded size ( ) exceeds declared ( )
Error message
uploaded size ({head.SizeBytes}) exceeds declared ({asset.SizeBytes}) What it means
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.
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
Example fix
// before
var size = file.Length; // captured earlier
await upload(url, file); // file has since grown
// after
await using var fs = File.OpenRead(path);
if (fs.Length != declaredSize) { /* re-request upload with new size */ }
await upload(url, fs); Defensive patterns
Strategy: validation
Validate before calling
if (file.size !== declaredSizeBytes) {
throw new Error(`file is ${file.size} bytes but ${declaredSizeBytes} were declared`);
} Try / catch
try { await api.finalizeUpload(assetId); }
catch (e) { if (e.status === 400 && /exceeds declared/.test(e.message)) { await requestNewUpload(file); } else throw e; } Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Declared size must be positive.
- uploaded content-type mismatch
- File exceeds max size of
- File exceeds max size of
- Cannot finalize file in status
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/d56705b6a7c07c58.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Files/Modules.Files/Features/v1/FinalizeUpload/FinalizeUploadCommandHandler.cs:60
{
throw new ForbiddenException("not your pending file");
}
if (asset.Status != FileAssetStatus.PendingUpload)
{
throw new CustomException("file already finalized", (IEnumerable<string>?)null, HttpStatusCode.Conflict);
}
var head = await storage.HeadObjectAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false)
?? throw new CustomException("upload not received", (IEnumerable<string>?)null, HttpStatusCode.Conflict);
// Allow declared+1% slack (S3 may differ slightly on multipart). Reject larger sizes.
var maxAllowed = asset.SizeBytes + Math.Max(1024L, asset.SizeBytes / 100);
if (head.SizeBytes > maxAllowed)
{
await storage.RemoveAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);
db.FileAssets.Remove(asset);
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
throw new CustomException(
$"uploaded size ({head.SizeBytes}) exceeds declared ({asset.SizeBytes})",
(IEnumerable<string>?)null,
HttpStatusCode.BadRequest);
}
if (!string.Equals(head.ContentType, asset.ContentType, StringComparison.OrdinalIgnoreCase))
{
await storage.RemoveAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);
db.FileAssets.Remove(asset);
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
throw new CustomException(
"uploaded content-type mismatch",
(IEnumerable<string>?)null,
HttpStatusCode.BadRequest);
}
var scanResult = await scanner.ScanAsync(asset.StorageKey, cancellationToken).ConfigureAwait(false);
asset.MarkAvailable(head.SizeBytes, scanResult);View on GitHub (pinned to 3f2959e683)