This page covers deleting one path, a list of paths, or everything under a prefix, and the calls that clean up multipart uploads that were never completed.
All three shapes return nothing and treat “already gone” as success. They differ in how many requests they make and what they throw when storage refuses part of the work. Method signatures and the target type: TypeScript, Python, Go.
A target that is none of the three shapes is refused with invalid_input. In Python that is passing both or neither of paths and prefix=; any other iterable, a dict included, is read as a list of paths. Go has a method per shape, so there is no target to get wrong. A path with a . or .. segment is rejected before any request is sent; see Paths.
The upload handler also deletes on its own: a throw out of the completion callback, or a cancel from the browser, removes the object that upload wrote. See Recording the upload.
One path#
One DELETE request. A 404 counts as success, so a delete is safe to run from a retried job or an at-least-once queue consumer. Any other failure throws; see Errors.
A delete never says whether anything was there. To find out, call exists first.
A list of paths#
Sent as batch deletes in chunks of 1000 paths, so a 5000-path list is five requests. In TypeScript a bad path fails the chunk it is in, and earlier chunks have already run. Python and Go check every path before the first request, so nothing is deleted.
When storage reports keys as failed, the SDK re-checks each one and keeps only the paths still there. If any survive, the delete throws partial_delete with them as its failed paths. Everything not in that list was deleted. To recover, retry with those paths.
A prefix#
Pages through list at 1000 objects per page and batch-deletes each page as it goes. A prefix with 100,000 objects is 100 list requests and 100 batch deletes, run one after another. It is not atomic; objects written under the prefix while it runs may or may not be caught.
Failures work as for a list. Survivors from every page are collected and thrown as partial_delete.
An empty prefix would match every object in the bucket, so it is refused with invalid_input. This protects against an unset variable or an empty form field. To wipe the bucket on purpose, say so with the all flag, or in Go with DeleteAll:
The all flag is only consulted for the empty prefix.
move leaves a copy on failure#
move is a copy followed by a delete, because storage has no rename. If the copy fails, nothing changed and you get the copy’s error. If the copy succeeds and the delete fails, move throws move_left_a_copy and keeps the destination, so you have two objects rather than none. The original error is on the error’s cause.
Incomplete multipart uploads#
A multipart upload becomes an object only when it is completed. Until then its parts are billed storage that list cannot see, and the bucket cannot be deleted while one exists. A browser tab closed mid-upload leaves exactly this behind.
put and a browser cancel abort their own uploads on failure. Anything else needs a sweep. The cron that runs it is on Abandoned uploads; the calls it uses are below. Signatures: TypeScript, Python, Go.
Listing incomplete uploads#
Returns every upload started and neither completed nor aborted, paging internally until it has them all. The prefix is optional.
| Field | Meaning |
|---|---|
| Path | The key the upload was started for. Nothing is stored there yet. |
| Upload id | Storage’s id for the upload, needed to abort it. |
| Initiated at | When it was started. What “stale” is measured against. |
Aborting one upload#
Throws the upload away with every part that landed for it. An upload that is already gone counts as success. An empty upload id is refused with invalid_input.
The completion callback receives the multipart upload id for exactly this pair. Store it with your row and you can abort a specific upload later without listing the bucket. It is not set for a single PUT.
Aborting stale uploads#
List plus abort in one call, meant for a cron. The older-than window is required, a Duration. In Go it is a time.Duration, and zero aborts every incomplete upload under the prefix. Only uploads started longer ago than that are touched, so a window longer than your slowest upload never aborts one still running. A day is a reasonable default.
An abandoned upload under the multipart threshold is not a multipart upload. It is an ordinary stored object that list can see, and none of the calls above can find it. See Abandoned uploads.
Error codes#
| Code | Raised by |
|---|---|
partial_delete | A list or prefix delete where objects survived. The error lists them. |
move_left_a_copy | move, when the source delete failed. |
invalid_input | A bad delete target, an empty prefix without the all flag, an empty upload id. |
A delete never raises not_found. Statuses and extra fields are on Errors.