bitwarden/server · error · BadRequestException

ReportFileId query parameter is required

Error message

ReportFileId query parameter is required

What it means

Thrown by the self-hosted file upload endpoint when the reportFileId query parameter is missing or empty. The controller needs to know which report-file entry to attach the uploaded bytes to; without it the upload is rejected before streaming begins.

Source

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

    /// <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);
        });

        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)

View on GitHub (pinned to e93b962371)

Solutions

  1. Append ?reportFileId=<id> (from the create-report response) to the upload URL.
  2. If lost, GET the report to recover the file entry id before uploading.
  3. Validate the query string is non-empty before issuing the POST.

Example fix

// before
var url = $"/reports/organizations/{orgId}/{reportId}/file";
// after
var url = $"/reports/organizations/{orgId}/{reportId}/file?reportFileId={Uri.EscapeDataString(reportFileId)}";
Defensive patterns

Strategy: validation

Validate before calling

if (string.IsNullOrWhiteSpace(reportFileId))
    throw new ArgumentException("reportFileId query parameter is required for upload.", nameof(reportFileId));

Try / catch

try { await client.PostAsync($".../file?reportFileId={reportFileId}", multipart); }
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("ReportFileId"))
{ /* recover id from GET report and retry */ }

Prevention

When it happens

Trigger: POST to /reports/organizations/{orgId}/{reportId}/file without ?reportFileId=...; the query param is present but empty.

Common situations: Client hardcodes the upload URL template without the query param; reportFileId lost between create and upload; URL-encoding bug drops the value.

Related errors


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