bitwarden/server · warning · BadRequestException
You can only unarchive up to 500 items at a time.
Error message
You can only unarchive up to 500 items at a time.
What it means
PUT /ciphers/unarchive (PutUnarchiveMany, bulk) rejects more than 500 ids on non-self-hosted (cloud) deployments and returns HTTP 400. The cap mirrors the other bulk operations; self-hosted bypasses it.
Source
Thrown at src/Api/Vault/Controllers/CiphersController.cs:1108
if (unarchivedCipherDetails.Count == 0)
{
throw new BadRequestException("Cipher was not unarchived. Ensure the provided ID is correct and you have permission to archive it.");
}
var unarchivedCipher = unarchivedCipherDetails.First();
return new CipherResponseModel(unarchivedCipher,
await _userService.GetUserByPrincipalAsync(User),
await GetOrganizationAbilityAsync(unarchivedCipher),
_globalSettings
);
}
[HttpPut("unarchive")]
public async Task<ListResponseModel<CipherResponseModel>> PutUnarchiveMany([FromBody] CipherBulkUnarchiveRequestModel model)
{
if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)
{
throw new BadRequestException("You can only unarchive up to 500 items at a time.");
}
var userId = _userService.GetProperUserId(User).Value;
var user = await _userService.GetUserByPrincipalAsync(User);
var cipherIdsToUnarchive = new HashSet<Guid>(model.Ids);
var unarchivedCipherOrganizationDetails = await _unarchiveCiphersCommand.UnarchiveManyAsync(cipherIdsToUnarchive, userId);
if (unarchivedCipherOrganizationDetails.Count == 0)
{
throw new BadRequestException("Ciphers were not unarchived. Ensure the provided ID is correct and you have permission to archive it.");
}
var organizationAbilities = await GetOrganizationAbilitiesAsync(unarchivedCipherOrganizationDetails);
var responses = unarchivedCipherOrganizationDetails.Select(cipher =>
new CipherResponseModel(cipher, user, GetOrganizationAbility(cipher, organizationAbilities), _globalSettings)).ToArray();
View on GitHub (pinned to e93b962371)
Solutions
- Chunk the id list into batches of <= 500 and issue multiple PUT /ciphers/unarchive calls.
- Surface the 500-item limit in the UI before triggering bulk unarchive.
- Note the cap is cloud-only; self-hosted is unaffected.
Example fix
// before
PUT /ciphers/unarchive body: { "ids": [/* 650 ids */] }
// after
PUT /ciphers/unarchive body: { "ids": first500 }
PUT /ciphers/unarchive body: { "ids": remaining150 } Defensive patterns
Strategy: validation
Validate before calling
const UNARCHIVE_LIMIT = 500;
async function bulkUnarchive(ids) {
for (const chunk of chunkBy(ids, UNARCHIVE_LIMIT)) {
await api.put('/ciphers/unarchive', { ids: chunk });
}
} Type guard
function isWithinBulkLimit(ids: unknown[], limit = 500): boolean {
return Array.isArray(ids) && ids.length <= limit;
} Prevention
- Chunk bulk unarchive to <= 500 ids on cloud.
- Surface the limit in the UI before mass unarchive.
- Remember self-hosted deployments are exempt.
When it happens
Trigger: A bulk PUT /ciphers/unarchive whose `Ids` contains more than 500 entries on a cloud deployment.
Common situations: A 'select all + unarchive' UI over a large vault; a restore script not chunking requests; bulk restore after a mass archive.
Related errors
- You can only archive up to 500 items at a time.
- You can only delete up to 500 items at a time. Consider usin
- You can only delete up to 500 items at a time.
- No ciphers were archived. Ensure the provided IDs are correc
- Cipher was not unarchived. Ensure the provided ID is correct
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/e39a8d45af9019d7.
Report an issue: GitHub.