bitwarden/server · warning · BadRequestException

You can only archive up to 500 items at a time.

Error message

You can only archive up to 500 items at a time.

What it means

PUT /ciphers/archive (PutArchiveMany, bulk) rejects requests with more than 500 ids when the deployment is NOT self-hosted (cloud only). The cap protects cloud throughput; self-hosted instances bypass it. Exceeding it returns HTTP 400.

Source

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

        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);

        var archivedCiphers = await _archiveCiphersCommand.ArchiveManyAsync(cipherIdsToArchive, userId);

        if (archivedCiphers.Count == 0)
        {
            throw new BadRequestException("No ciphers were archived. Ensure the provided IDs are correct and you have permission to archive them.");
        }

        var organizationAbilities = await GetOrganizationAbilitiesAsync(archivedCiphers);
        var responses = archivedCiphers.Select(cipher =>
            new CipherResponseModel(cipher, user, GetOrganizationAbility(cipher, organizationAbilities), _globalSettings)).ToArray();

View on GitHub (pinned to e93b962371)

Solutions

  1. Chunk the id list into batches of <= 500 and issue multiple requests.
  2. Surface the 500-item limit in the UI before the user triggers a bulk archive.
  3. If operating self-hosted, note the cap does not apply there.

Example fix

// before
PUT /ciphers/archive  body: { "ids": [/* 750 ids */] }
// after
PUT /ciphers/archive  body: { "ids": first500 }
PUT /ciphers/archive  body: { "ids": remaining250 }
Defensive patterns

Strategy: validation

Validate before calling

const ARCHIVE_LIMIT = 500;
async function bulkArchive(ids) {
  for (const chunk of chunkBy(ids, ARCHIVE_LIMIT)) {
    await api.put('/ciphers/archive', { ids: chunk });
  }
}
function chunkBy(arr, n) { const out = []; for (let i = 0; i < arr.length; i += n) out.push(arr.slice(i, i + n)); return out; }

Type guard

function isWithinBulkLimit(ids: unknown[], limit = 500): boolean {
  return Array.isArray(ids) && ids.length <= limit;
}

Prevention

When it happens

Trigger: A bulk PUT /ciphers/archive whose `Ids` collection contains more than 500 entries on a cloud (non-self-hosted) deployment.

Common situations: A 'select all + archive' UI action over a large vault; a migration script archiving many items in one call; a client not chunking bulk operations.

Related errors


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