{"record":{"id":"4a9b89055ef38e5c","repo":"bitwarden/server","slug":"file-received-does-not-match-expected-constraints","errorCode":null,"errorMessage":"File received does not match expected constraints.","messagePattern":"File received does not match expected constraints\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Dirt/Controllers/OrganizationReportsController.cs","lineNumber":420,"sourceCode":"            throw new NotFoundException();\n        }\n\n        await Request.GetFileAsync(async (stream) =>\n        {\n            await _storageService.UploadReportDataAsync(report, fileData, stream);\n        });\n\n        var leeway = 1024L * 1024L; // 1 MB\n        var minimum = Math.Max(0, fileData.Size - leeway);\n        var maximum = Math.Min(fileData.Size + leeway, Constants.FileSize501mb);\n        var (valid, length) = await _storageService.ValidateFileAsync(report, fileData, minimum, maximum);\n        if (!valid)\n        {\n            await _storageService.DeleteReportFilesAsync(report, fileData.Id!);\n            await _organizationReportRepo.DeleteAsync(report);\n            await _cache.RemoveByTagAsync(\n                OrganizationReportCacheConstants.BuildCacheTagForOrganizationReports(organizationId));\n            throw new BadRequestException(\"File received does not match expected constraints.\");\n        }\n\n        fileData.Validated = true;\n        fileData.Size = length;\n        report.SetReportFile(fileData);\n        report.RevisionDate = DateTime.UtcNow;\n        await _organizationReportRepo.ReplaceAsync(report);\n        await _cache.RemoveByTagAsync(\n            OrganizationReportCacheConstants.BuildCacheTagForOrganizationReports(organizationId));\n    }\n\n    /// <summary>\n    /// Downloads an organization report file for a self-hosted instance.\n    /// Validates that the organization ID and report ID are non-empty,\n    /// then authorizes the caller via <see cref=\"AuthorizeAsync\"/>.\n    /// Verifies the report exists and belongs to the specified organization.\n    /// Retrieves the file metadata and streams the file from local storage.\n    /// Cloud-hosted instances download files directly from Azure Blob Storage","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Dirt/Controllers/OrganizationReportsController.cs#L402-L438","documentation":"Thrown after the self-hosted upload stream completes when ValidateFileAsync reports the received file size is outside the accepted window: [fileData.Size − 1MB, min(fileData.Size + 1MB, 501MB)]. On failure the controller deletes the uploaded blob, deletes the report record, and evicts the reports cache before throwing — so the report is fully rolled back.","triggerScenarios":"The actual uploaded byte count differs from the FileSize declared at create-report time by more than 1 MiB in either direction; the upload was truncated (network drop, proxy limit) or padded/corrupted.","commonSituations":"Client computes FileSize from the source file but uploads a transformed/compressed version; network interruption truncates the multipart body but the connection appears to close cleanly; intermediary (reverse proxy, WAF) strips or adds bytes; client retries upload to the same report with a different-sized file.","solutions":["Measure the exact byte length of the bytes you actually stream and set FileSize to that same value at create-report time.","If the source changed, create a NEW report (new FileSize) rather than re-uploading to the existing one.","Check for proxy/WAF body-size limits between client and server that could truncate the upload."],"exampleFix":"// before: FileSize from source file, upload from compressed stream\nvar req = new AddOrganizationReportRequestModel { FileSize = new FileInfo(srcPath).Length };\n// after: measure the exact bytes you upload\nusing var ms = new MemoryStream();\nCompressTo(srcPath, ms);\nvar req = new AddOrganizationReportRequestModel { FileSize = ms.Length };","handlingStrategy":"validation","validationCode":"// Set FileSize to the exact byte count of the stream you will upload\nusing var ms = new MemoryStream();\nawait PrepareUploadStreamAsync(source, ms);\nvar fileSize = ms.Length;\n// pass fileSize as request.FileSize at create-report time","typeGuard":null,"tryCatchPattern":"try { await client.PostAsync(uploadUrl, multipart); }\ncatch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains(\"constraints\"))\n{ /* report was rolled back — recreate with correct FileSize and re-upload */ }","preventionTips":["Measure the actual upload byte length and use it as FileSize.","Never re-upload a different-sized file to the same report.","Watch for proxy/WAF body truncation between client and server."],"tags":["validation","file-upload","size-mismatch","rollback","csharp","aspnetcore"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}