bitwarden/server · error · BadRequestException

Invalid report ID

Error message

Invalid report ID

What it means

Thrown by GetAuthorizedReportAsync (the shared helper behind report-by-id endpoints) when the fetched report's OrganizationId does not equal the organizationId in the request path. The report exists but belongs to a different organization — a cross-org access attempt, surfaced as 400 'Invalid report ID' rather than 404.

Source

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

    private static void EnsureValidIds(Guid organizationId, Guid? reportId = null)
    {
        if (organizationId == Guid.Empty)
        {
            throw new BadRequestException("OrganizationId is required.");
        }

        if (reportId.HasValue && reportId.Value == Guid.Empty)
        {
            throw new BadRequestException("ReportId is required.");
        }
    }

    private async Task<OrganizationReport> GetAuthorizedReportAsync(Guid organizationId, Guid reportId)
    {
        EnsureValidIds(organizationId, reportId);
        await AuthorizeAsync(organizationId);
        var report = await _getOrganizationReportQuery.GetOrganizationReportAsync(reportId);
        if (report.OrganizationId != organizationId) throw new BadRequestException("Invalid report ID");
        return report;
    }


    // Is being used by client on V2

    [HttpGet("{organizationId}/data/summary/{reportId}")]
    public async Task<IActionResult> GetOrganizationReportSummaryAsync(Guid organizationId, Guid reportId)
    {
        EnsureValidIds(organizationId, reportId);

        await AuthorizeAsync(organizationId);

        var summaryData =
            await _getOrganizationReportSummaryDataQuery.GetOrganizationReportSummaryDataAsync(organizationId, reportId);

        if (summaryData == null)
        {

View on GitHub (pinned to e93b962371)

Solutions

  1. Ensure the reportId and organizationId in the request come from the same report record.
  2. Re-fetch the report list for the intended org to obtain the correct reportId.
  3. Client-side, scope report ids per organization to avoid cross-contamination.

Example fix

// before: reportId sourced from another org
var url = $"/reports/organizations/{orgB}/{reportFromOrgA}";
// after
var report = await ListReportsAsync(orgB).First(r => /* match */);
var url = $"/reports/organizations/{orgB}/{report.Id}";
Defensive patterns

Strategy: validation

Validate before calling

var report = await GetReportAsync(reportId);
if (report.OrganizationId != organizationId)
    throw new InvalidOperationException("Report does not belong to the specified organization.");

Try / catch

try { await client.GetAsync($"/reports/organizations/{orgId}/{reportId}"); }
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest && ex.Message.Contains("Invalid report ID"))
{ /* reportId belongs to a different org — re-fetch the correct id */ }

Prevention

When it happens

Trigger: Client supplies a valid reportId that belongs to org A but pairs it with org B's organizationId in the URL. The report is loaded by reportId alone, then the org ownership is compared and mismatched.

Common situations: Copy-paste of a reportId across organizations; client mixed up ids from multiple orgs; URL built from two different sources; test fixture reused a reportId across orgs.

Related errors


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