bitwarden/server · error · NotFoundException

Resource not found.

Error message

Resource not found.

What it means

Thrown as NotFoundException (HTTP 404) from PUT /emergency-access/{id}. _emergencyAccessRepository.GetByIdAsync(id) returns null — no emergency access record exists with the given GUID. The controller checks existence before allowing the grantor to update the access configuration (e.g., key type, wait time).

Source

Thrown at src/Api/Auth/Controllers/EmergencyAccessController.cs:87

        return new EmergencyAccessGranteeDetailsResponseModel(result);
    }

    [HttpGet("{id}/policies")]
    public async Task<ListResponseModel<PolicyResponseModel>> Policies(Guid id)
    {
        var user = await _userService.GetUserByPrincipalAsync(User);
        var policies = await _emergencyAccessService.GetPoliciesAsync(id, user);
        var responses = policies?.Select(policy => new PolicyResponseModel(policy));
        return new ListResponseModel<PolicyResponseModel>(responses);
    }

    [HttpPut("{id}")]
    public async Task Put(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
    {
        var emergencyAccess = await _emergencyAccessRepository.GetByIdAsync(id);
        if (emergencyAccess == null)
        {
            throw new NotFoundException();
        }

        var user = await _userService.GetUserByPrincipalAsync(User);
        await _emergencyAccessService.SaveAsync(model.ToEmergencyAccess(emergencyAccess), user);
    }

    [HttpPost("{id}")]
    [Obsolete("This endpoint is deprecated. Use PUT /{id} instead.")]
    public async Task Post(Guid id, [FromBody] EmergencyAccessUpdateRequestModel model)
    {
        await Put(id, model);
    }

    [HttpDelete("{id}")]
    public async Task Delete(Guid id)
    {
        var userId = _userService.GetProperUserId(User);
        await _emergencyAccessService.DeleteAsync(id, userId.Value);

View on GitHub (pinned to e93b962371)

Solutions

  1. Call GET /emergency-access to list the user's current emergency access records and use a valid id.
  2. If the record was deleted, create a new emergency access invite via POST /emergency-access.
  3. Verify the GUID in the URL is complete and correctly formatted.

Example fix

// before: updating a deleted/nonexistent emergency access record
var resp = await client.PutAsJsonAsync($"/emergency-access/{oldId}", model); // 404

// after: list current records first
var list = await client.GetAsync("/emergency-access");
var currentId = SelectEmergencyAccessId(list);
var resp = await client.PutAsJsonAsync($"/emergency-access/{currentId}", model);
Defensive patterns

Strategy: validation

Validate before calling

// Verify the emergency access record exists before updating
var list = await client.GetAsync("/emergency-access");
var validIds = ParseEmergencyAccessIds(list);
if (!validIds.Contains(id)) {
    return Error("Emergency access record not found. It may have been revoked or deleted.");
}

Try / catch

try {
    var resp = await client.PutAsJsonAsync($"/emergency-access/{id}", model);
    resp.EnsureSuccessStatusCode();
} catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound) {
    // Record doesn't exist — refresh the list
    await RefreshEmergencyAccessListAsync();
    ShowUserError("This emergency access record no longer exists.");
}

Prevention

When it happens

Trigger: PUT /emergency-access/{id} is called with a GUID that does not correspond to any emergency access invite/grant. The record may have been deleted, the id was never valid, or was transcribed incorrectly.

Common situations: The emergency access invitation was revoked or declined before the update. The grantee or grantor deleted the emergency access relationship. Client uses a cached id from a previous session after the record was removed. GUID was mistyped or truncated in the request URL.

Related errors


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