bitwarden/server · error · BadRequestException

File received does not match expected constraints.

Error message

File received does not match expected constraints.

What it means

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.

Solutions

  1. Measure the exact byte length of the bytes you actually stream and set FileSize to that same value at create-report time.
  2. If the source changed, create a NEW report (new FileSize) rather than re-uploading to the existing one.
  3. Check for proxy/WAF body-size limits between client and server that could truncate the upload.

Example fix

// before: FileSize from source file, upload from compressed stream
var req = new AddOrganizationReportRequestModel { FileSize = new FileInfo(srcPath).Length };
// after: measure the exact bytes you upload
using var ms = new MemoryStream();
CompressTo(srcPath, ms);
var req = new AddOrganizationReportRequestModel { FileSize = ms.Length };
Defensive patterns

Strategy: validation

Validate before calling

// Set FileSize to the exact byte count of the stream you will upload
using var ms = new MemoryStream();
await PrepareUploadStreamAsync(source, ms);
var fileSize = ms.Length;
// pass fileSize as request.FileSize at create-report time

Try / catch

try { await client.PostAsync(uploadUrl, multipart); }
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("constraints"))
{ /* report was rolled back — recreate with correct FileSize and re-upload */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/4a9b89055ef38e5c. Report an issue: GitHub.

Appendix: source

Thrown at src/Api/Dirt/Controllers/OrganizationReportsController.cs:420

            throw new NotFoundException();
        }

        await Request.GetFileAsync(async (stream) =>
        {
            await _storageService.UploadReportDataAsync(report, fileData, stream);
        });

        var leeway = 1024L * 1024L; // 1 MB
        var minimum = Math.Max(0, fileData.Size - leeway);
        var maximum = Math.Min(fileData.Size + leeway, Constants.FileSize501mb);
        var (valid, length) = await _storageService.ValidateFileAsync(report, fileData, minimum, maximum);
        if (!valid)
        {
            await _storageService.DeleteReportFilesAsync(report, fileData.Id!);
            await _organizationReportRepo.DeleteAsync(report);
            await _cache.RemoveByTagAsync(
                OrganizationReportCacheConstants.BuildCacheTagForOrganizationReports(organizationId));
            throw new BadRequestException("File received does not match expected constraints.");
        }

        fileData.Validated = true;
        fileData.Size = length;
        report.SetReportFile(fileData);
        report.RevisionDate = DateTime.UtcNow;
        await _organizationReportRepo.ReplaceAsync(report);
        await _cache.RemoveByTagAsync(
            OrganizationReportCacheConstants.BuildCacheTagForOrganizationReports(organizationId));
    }

    /// <summary>
    /// Downloads an organization report file for a self-hosted instance.
    /// Validates that the organization ID and report ID are non-empty,
    /// then authorizes the caller via <see cref="AuthorizeAsync"/>.
    /// Verifies the report exists and belongs to the specified organization.
    /// Retrieves the file metadata and streams the file from local storage.
    /// Cloud-hosted instances download files directly from Azure Blob Storage

View on GitHub (pinned to e93b962371)