CLOUD-4771: reject an empty id list in the bulk delete statics - #245
Merged
Merged
Conversation
Box.delete({ boxIds: [] }) sent {"ids": []}, and the API read an empty list as
"no filter", so it deleted every box on the account. Box.deleteSnapshots had
the same shape for snapshotIds: [].
Both now throw a BoxError before any request when the list is empty or holds a
blank id. deleteSnapshots() with no ids still deletes every snapshot, as
documented, and now sends ?all=true so the API does not have to infer
"everything" from a missing list.
The Python SDK mirrors both changes.
buggyhunter
approved these changes
Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linear: CLOUD-4771
Why
Found while investigating an incident where a console bug deleted every box on a customer's account. The root cause there was the console sending an unscoped
DELETE /v2/box, which the coordinator reads as "delete everything". The SDK can produce the same request:The coordinator treats an empty
idsas "no filter". A script that computes its id list and comes up empty wipes the account instead of doing nothing.Box.deleteSnapshots({ snapshotIds: [] })has the same shape, and snapshots are the only way to recover a deleted box's workspace.What changes
Box.deleteandBox.deleteSnapshotsthrow aBoxErrorbefore any request when the id list is empty or contains a blank id (sharedrequireIdshelper).EphemeralBox.delete/EphemeralBox.deleteSnapshotsare aliases and are covered.Box.deleteSnapshots()with nosnapshotIdsstill deletes every snapshot, as documented. It now sends?all=true, so "everything" is stated rather than inferred from a missing list.delete_boxes/delete_snapshotsraiseBoxErrorviacommon.require_ids, anddelete_snapshots()sendsall=true._syncis regenerated.Nothing in this repo passed an empty list: the only callers of the static
Box.deleteare inpackages/box-pi, one with a single id and one behind alength > 0check.Compatibility
upstash/box-backend#268 makes the coordinator reject empty and malformed
idsand accept?all=true. This PR works against the coordinator both before and after that change: the current coordinator ignores the unknownallparam and still deletes all snapshots for a request with no ids. #268 deliberately keeps accepting the old baredeleteSnapshots()shape so published SDK versions do not break; once this release is adopted, that fallback can be removed.Testing
box-delete.test.tsandbox-delete-snapshots.test.ts(empty array, empty string, blank id in a list, missingboxIds; each assertsfetchwas never called), plus the?all=trueURL.pnpm ci:lint,pnpm build, andpnpm testpass.all=trueassertion.ruff,mypy,pytest tests/_async tests/_sync(264 passed),check_parity.py, and sync generation are clean.UPSTASH_BOX_API_KEY).patchfor@upstash/box.