{"record":{"id":"e54195f20ed1dc21","repo":"bitwarden/server","slug":"you-can-only-delete-up-to-500-items-at-a-time","errorCode":null,"errorMessage":"You can only delete up to 500 items at a time.","messagePattern":"You can only delete up to 500 items at a time\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"src/Api/Vault/Controllers/CiphersController.cs","lineNumber":1051,"sourceCode":"    public async Task PutDeleteAdmin(Guid id)\n    {\n        var userId = _userService.GetProperUserId(User).Value;\n        var cipher = await GetByIdAsyncAdmin(id);\n        if (cipher == null || !cipher.OrganizationId.HasValue ||\n            !await CanDeleteOrRestoreCipherAsAdminAsync(cipher.OrganizationId.Value, new[] { cipher.Id }))\n        {\n            throw new NotFoundException();\n        }\n\n        await _cipherService.SoftDeleteAsync(new CipherDetails(cipher), userId, true);\n    }\n\n    [HttpPut(\"delete\")]\n    public async Task PutDeleteMany([FromBody] CipherBulkDeleteRequestModel model)\n    {\n        if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)\n        {\n            throw new BadRequestException(\"You can only delete up to 500 items at a time.\");\n        }\n\n        var userId = _userService.GetProperUserId(User).Value;\n        await _cipherService.SoftDeleteManyAsync(model.Ids.Select(i => new Guid(i)), userId);\n    }\n\n    [HttpPut(\"delete-admin\")]\n    public async Task PutDeleteManyAdmin([FromBody] CipherBulkDeleteRequestModel model)\n    {\n        if (!_globalSettings.SelfHosted && model.Ids.Count() > 500)\n        {\n            throw new BadRequestException(\"You can only delete up to 500 items at a time.\");\n        }\n\n        if (model == null)\n        {\n            throw new NotFoundException();\n        }","sourceCodeStart":1033,"sourceCodeEnd":1069,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Vault/Controllers/CiphersController.cs#L1033-L1069","documentation":"PUT /ciphers/delete (PutDeleteMany, bulk soft-delete) rejects more than 500 ids on non-self-hosted (cloud) deployments and returns HTTP 400. Unlike the hard-delete variants, no Purge suggestion is appended to this message.","triggerScenarios":"A bulk PUT /ciphers/delete whose `Ids` contains more than 500 entries on a cloud deployment.","commonSituations":"A 'select all + soft-delete' UI over a large vault; a script soft-deleting many items; not chunking the request.","solutions":["Chunk the id list into batches of <= 500 and issue multiple PUT /ciphers/delete calls.","Surface the 500-item limit in the UI before the user triggers bulk soft-delete.","Note the cap applies only to cloud, not self-hosted."],"exampleFix":"// before\nPUT /ciphers/delete  body: { \"ids\": [/* 700 ids */] }\n// after\nPUT /ciphers/delete  body: { \"ids\": first500 }\nPUT /ciphers/delete  body: { \"ids\": remaining200 }","handlingStrategy":"validation","validationCode":"const SOFT_DELETE_LIMIT = 500;\nasync function bulkSoftDelete(ids) {\n  for (const chunk of chunkBy(ids, SOFT_DELETE_LIMIT)) {\n    await api.put('/ciphers/delete', { ids: chunk });\n  }\n}","typeGuard":"function isWithinBulkLimit(ids: unknown[], limit = 500): boolean {\n  return Array.isArray(ids) && ids.length <= limit;\n}","tryCatchPattern":null,"preventionTips":["Chunk soft-delete batches to <= 500 ids on cloud.","Show the limit in the UI before mass soft-delete.","Note self-hosted deployments bypass the cap."],"tags":["cipher","delete","soft-delete","bulk","limit","cloud","bad-request"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}