bitwarden/server · error · BadRequestException
Invalid content.
Error message
Invalid content.
What it means
Thrown by POST /reports/organizations/{organizationId}/{reportId}/file (self-hosted upload endpoint) when the request Content-Type does not contain 'multipart/'. The endpoint uses DisableFormValueModelBinding and Request.GetFileAsync to stream the multipart body, so a non-multipart request cannot be parsed. Self-hosted only.
Source
Thrown at src/Api/Dirt/Controllers/OrganizationReportsController.cs:391
/// Uploads a report data file for a self-hosted organization report via multipart form data.
/// Validates the uploaded file size against the expected size (with a 1 MB leeway) and marks
/// the report file as validated upon success. Requires the Access Intelligence new-architecture feature flag.
/// </summary>
/// <param name="organizationId">The unique identifier of the organization.</param>
/// <param name="reportId">The unique identifier of the report to attach the file to.</param>
/// <param name="reportFileId">The identifier of the report file entry to upload against.</param>
[RequireFeature(FeatureFlagKeys.AccessIntelligenceNewArchitecture)]
[HttpPost("{organizationId}/{reportId}/file")]
[SelfHosted(SelfHostedOnly = true)]
[RequestSizeLimit(Constants.FileSize501mb)]
[DisableFormValueModelBinding]
public async Task UploadReportFileAsync(Guid organizationId, Guid reportId, [FromQuery] string reportFileId)
{
var report = await GetAuthorizedReportAsync(organizationId, reportId);
if (!Request?.ContentType?.Contains("multipart/") ?? true)
{
throw new BadRequestException("Invalid content.");
}
if (string.IsNullOrEmpty(reportFileId))
{
throw new BadRequestException("ReportFileId query parameter is required");
}
var fileData = report.GetReportFile();
if (fileData == null || fileData.Id != reportFileId || fileData.Validated)
{
throw new NotFoundException();
}
await Request.GetFileAsync(async (stream) =>
{
await _storageService.UploadReportDataAsync(report, fileData, stream);
});
View on GitHub (pinned to e93b962371)
Solutions
- Send the file as multipart/form-data: in .NET use MultipartFormDataContent; in curl use -F 'file=@path'.
- Ensure the boundary parameter is present in Content-Type (most HTTP clients add it automatically when using multipart helpers).
- Verify no intermediary proxy rewrites or strips the Content-Type header.
Example fix
// before var content = new StreamContent(fileStream); // Content-Type: application/octet-stream await client.PostAsync(uploadUrl, content); // after using var form = new MultipartFormDataContent(); form.Add(new StreamContent(fileStream), "file", fileName); await client.PostAsync(uploadUrl, form);
Defensive patterns
Strategy: validation
Validate before calling
var contentType = httpClient.DefaultRequestHeaders; // ensure per-request content type
if (!content.Headers.ContentType?.MediaType?.StartsWith("multipart/") ?? true)
throw new InvalidOperationException("Upload must be multipart/form-data."); Try / catch
try { await client.PostAsync(uploadUrl, multipartContent); }
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("Invalid content"))
{ /* rebuild as MultipartFormDataContent and retry */ } Prevention
- Always use MultipartFormDataContent (or curl -F) for file uploads.
- Let the HTTP client set the boundary automatically.
- Verify proxies don't strip the Content-Type header.
When it happens
Trigger: Client POSTs the file with Content-Type application/octet-stream, application/json, or omits Content-Type entirely instead of multipart/form-data with a proper boundary.
Common situations: Using HttpClient.PostAsync with raw StreamContent instead of MultipartFormDataContent; curl invocation missing -F; proxy stripping the Content-Type header or boundary; client library defaulting to JSON for binary payloads.
Related errors
- Max file size is 500 MB.
- ReportFileId is required.
- Resource not found.
- ReportFileId query parameter is required
- File received does not match expected constraints.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/587b0b521779411d.
Report an issue: GitHub.