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
- Ensure the reportId and organizationId in the request come from the same report record.
- Re-fetch the report list for the intended org to obtain the correct reportId.
- 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
- Always pair a reportId with the organizationId of the org that owns it.
- Scope stored report ids per organization to avoid mix-ups.
- On this 400, re-list reports for the intended org to get the right id.
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
- OrganizationId is required.
- ReportId is required.
- Resource not found.
- Max file size is 500 MB.
- ReportFileId is required.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/7ae55a60ab428129.
Report an issue: GitHub.