bitwarden/server · error · BadRequestException

Cipher was not archived. Ensure the provided ID is correct…

Error message

Cipher was not archived. Ensure the provided ID is correct and you have permission to archive it.

What it means

PUT /ciphers/{id}/archive (PutArchive) delegates to ArchiveManyAsync with a single id. If zero ciphers are archived — wrong id, not owned by the user, no edit permission, or an archived/deleted state — the server returns HTTP 400 telling the caller the id or permission is wrong.

Solutions

  1. Verify the cipher id is correct and that you own or can edit the cipher.
  2. Re-sync the vault to confirm the cipher exists and isn't already archived/deleted.
  3. If it is an org cipher, confirm your collection/org permissions grant edit rights.
Defensive patterns

Strategy: validation

Validate before calling

async function safeArchive(id) {
  const c = await api.get(`/ciphers/${id}`).catch(() => null);
  if (!c || !c.editable) throw new Error('Cipher missing or not editable; cannot archive');
  return api.put(`/ciphers/${id}/archive`);
}

Type guard

function isArchivable(cipher: { id?: string; editable?: boolean; deletedDate?: string | null } | null): boolean {
  return !!cipher && !!cipher.id && cipher.editable === true && !cipher.deletedDate;
}

Try / catch

try { await api.put(`/ciphers/${id}/archive`); }
catch (e) {
  if (e?.response?.status === 400 && /not archived/i.test(e.response.data?.message ?? '')) {
    await syncVault(); // re-check ownership/permissions, do not blindly retry
  } else throw e;
}

Prevention

When it happens

Trigger: PUT /ciphers/{id}/archive where the id does not exist, belongs to another user, or the user lacks permission to archive it.

Common situations: Archiving a cipher that was deleted by another client; archiving an org cipher without edit rights; a stale/outdated cipher id after sync.

Related errors


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

Appendix: source

Thrown at src/Api/Vault/Controllers/CiphersController.cs:892

        {
            await _collectionCipherRepository.RemoveCollectionsForManyCiphersAsync(model.OrganizationId, model.CipherIds, model.CollectionIds);
        }
        else
        {
            await _collectionCipherRepository.AddCollectionsForManyCiphersAsync(model.OrganizationId, model.CipherIds, model.CollectionIds);
        }
    }

    [HttpPut("{id}/archive")]
    public async Task<CipherResponseModel> PutArchive(Guid id)
    {
        var userId = _userService.GetProperUserId(User).Value;

        var archivedCipherOrganizationDetails = await _archiveCiphersCommand.ArchiveManyAsync([id], userId);

        if (archivedCipherOrganizationDetails.Count == 0)
        {
            throw new BadRequestException("Cipher was not archived. Ensure the provided ID is correct and you have permission to archive it.");
        }

        var archivedCipher = archivedCipherOrganizationDetails.First();
        return new CipherResponseModel(archivedCipher, await _userService.GetUserByPrincipalAsync(User), await GetOrganizationAbilityAsync(archivedCipher), _globalSettings);
    }

    [HttpPut("archive")]
    public async Task<ListResponseModel<CipherResponseModel>> PutArchiveMany([FromBody] CipherBulkArchiveRequestModel model)
    {
        if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)
        {
            throw new BadRequestException("You can only archive up to 500 items at a time.");
        }

        var userId = _userService.GetProperUserId(User).Value;
        var user = await _userService.GetUserByPrincipalAsync(User);

        var cipherIdsToArchive = new HashSet<Guid>(model.Ids);

View on GitHub (pinned to e93b962371)