bitwarden/server · error · NotFoundException

Resource not found.

Error message

Resource not found.

What it means

Thrown by GetMemberCipherDetails when the calling user lacks the AccessReports permission for the given orgId. Bitwarden deliberately returns 404 (NotFoundException with no message) instead of 403 to avoid leaking the existence of the organization or the endpoint to unauthorized users. The permission check _currentContext.AccessReports(orgId) returns false.

Source

Thrown at src/Api/Dirt/Controllers/ReportsController.cs:66

        _getPasskeyDirectoryQuery = getPasskeyDirectoryQuery;
        _logger = logger;
    }

    /// <summary>
    /// Organization member information containing a list of cipher ids
    /// assigned
    /// </summary>
    /// <param name="orgId">Organzation Id</param>
    /// <returns>IEnumerable of MemberCipherDetailsResponseModel</returns>
    /// <exception cref="NotFoundException">If Access reports permission is not assigned</exception>
    [HttpGet("member-cipher-details/{orgId}")]
    public async Task<IEnumerable<MemberCipherDetailsResponseModel>> GetMemberCipherDetails(Guid orgId)
    {
        // Using the AccessReports permission here until new permissions
        // are needed for more control over reports
        if (!await _currentContext.AccessReports(orgId))
        {
            throw new NotFoundException();
        }

        var riskDetails = await GetRiskInsightsReportDetails(new RiskInsightsReportRequest { OrganizationId = orgId });

        var responses = riskDetails.Select(x => new MemberCipherDetailsResponseModel(x));

        return responses;
    }

    /// <summary>
    /// Access details for an organization member. Includes the member information,
    /// group collection assignment, and item counts
    /// </summary>
    /// <param name="orgId">Organization Id</param>
    /// <returns>IEnumerable of MemberAccessReportResponseModel</returns>
    /// <exception cref="NotFoundException">If Access reports permission is not assigned</exception>
    [HttpGet("member-access/{orgId}")]
    public async Task<IEnumerable<MemberAccessDetailReportResponseModel>> GetMemberAccessReport(Guid orgId)

View on GitHub (pinned to e93b962371)

Solutions

  1. Grant the AccessReports permission to the user's role in the organization admin panel.
  2. Verify the calling user is an active member of the target organization.
  3. If using a service account or API key, ensure it was created with reports-scoped access.
  4. Confirm the orgId in the URL matches an organization the user actually belongs to.

Example fix

// before
var details = await api.GetMemberCipherDetailsAsync(orgId);

// after — check access first
var perms = await api.GetMyPermissionsAsync(orgId);
if (!perms.Contains("access_reports"))
{
    // surface a clear auth error instead of hitting the opaque 404
    throw new UnauthorizedAccessException("AccessReports permission required.");
}
var details = await api.GetMemberCipherDetailsAsync(orgId);
Defensive patterns

Strategy: validation

Validate before calling

// Check permissions before calling the report endpoint
var access = await _currentContext.AccessReports(orgId);
if (!access)
    return Forbid("AccessReports permission is required.");
var details = await _reportQuery.GetMemberCipherDetailsAsync(orgId);

Type guard

public static bool HasReportsAccess(CurrentContext ctx, Guid orgId) =>
    ctx.Permissions.TryGetValue(orgId, out var p) && p.AccessReports;

Try / catch

try
{
    var details = await _reportService.GetMemberCipherDetailsAsync(orgId);
}
catch (NotFoundException)
{
    // 404 may mean either no access or no data — check permissions separately
    if (!await _currentContext.AccessReports(orgId))
        return Forbid();
    return NotFound();
}

Prevention

When it happens

Trigger: GET /reports/member-cipher-details/{orgId} called by a user who is not an org admin/owner, or whose role does not include the AccessReports permission, or who is not a member of the organization at all.

Common situations: A custom-role user whose permissions were recently trimmed; a service account token that was provisioned without reports access; a user removed from the org but still holding a stale session; testing against the wrong orgId.

Related errors


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