bitwarden/server · error · NotFoundException

Resource not found.

Error message

Resource not found.

What it means

Thrown by the file/renew endpoint when the report's file data is null, the reportFileId doesn't match the stored file's Id, or the file has already been Validated (no renewal needed). Reported as 404 to avoid leaking report state. Renewal only makes sense for a pending upload that hasn't been validated yet.

Source

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

    /// <param name="reportId">The unique identifier of the report with the pending file upload.</param>
    /// <param name="reportFileId">The identifier of the report file entry to renew the upload URL for.</param>
    /// <returns>An <see cref="OrganizationReportFileResponseModel"/> with the renewed upload URL.</returns>
    [RequireFeature(FeatureFlagKeys.AccessIntelligenceNewArchitecture)]
    [HttpGet("{organizationId}/{reportId}/file/renew")]
    public async Task<OrganizationReportFileResponseModel> RenewFileUploadUrlAsync(
        Guid organizationId, Guid reportId, [FromQuery] string reportFileId)
    {
        var report = await GetAuthorizedReportAsync(organizationId, reportId);

        if (string.IsNullOrEmpty(reportFileId))
        {
            throw new BadRequestException("ReportFileId is required.");
        }

        var fileData = report.GetReportFile();
        if (fileData == null || fileData.Id != reportFileId || fileData.Validated)
        {
            throw new NotFoundException();
        }

        return new OrganizationReportFileResponseModel
        {
            ReportFileUploadUrl = await _storageService.GetReportFileUploadUrlAsync(report, fileData),
            ReportResponse = new OrganizationReportResponseModel(report),
            FileUploadType = _storageService.FileUploadType
        };
    }

    /// <summary>
    /// Handles Azure Event Grid webhook notifications for blob storage events.
    /// When a <c>Microsoft.Storage.BlobCreated</c> event is received, validates the uploaded
    /// report file against the corresponding organization report. Orphaned blobs (with no
    /// matching report) are deleted. Requires the Access Intelligence new-architecture feature flag.
    /// This endpoint is anonymous to allow Azure Event Grid to call it directly.
    /// </summary>
    /// <returns>An <see cref="ObjectResult"/> acknowledging the Event Grid event.</returns>

View on GitHub (pinned to e93b962371)

Solutions

  1. Use the exact reportFileId returned by create-report; do not substitute or truncate it.
  2. Before renewing, GET the report and check the file's Validated flag — if true, no renew is needed (use download instead).
  3. If GetReportFile() is null, the report has no file — recreate the report with a FileSize to start a new upload.

Example fix

// before: blind renew
var resp = await client.GetAsync(renewUrl);
// after: check state first
var report = await GetReportAsync(orgId, reportId);
var f = report.ReportFile;
if (f == null || f.Validated) return; // nothing to renew
var resp = await client.GetAsync($".../file/renew?reportFileId={f.Id}");
Defensive patterns

Strategy: validation

Validate before calling

var report = await GetReportAsync(orgId, reportId);
var f = report.ReportFile;
if (f is null || f.Id != reportFileId || f.Validated)
    throw new InvalidOperationException("Report file not eligible for URL renewal.");

Try / catch

try { await client.GetAsync(renewUrl); }
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{ /* file missing, id mismatch, or already validated — re-fetch report to decide */ }

Prevention

When it happens

Trigger: reportFileId in the query doesn't match report.GetReportFile().Id; the report has no file attached; the file was already validated (Validated==true) so there's nothing pending to renew.

Common situations: Client renews the wrong report's file id; upload already completed via the Event Grid webhook (cloud) or the self-hosted upload endpoint, marking Validated=true, but client retries renew; race where validation lands between create and renew.

Related errors


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