Bulk actions

Pass an array instead of a key to act on many objects in one call - a bounded fan-out that keeps successes and failures separate, in input order, and never sinks the whole batch on one bad key.

upload, download, head, and exists each take a single key or an array; delete takes one key or many. The array form fans out with bounded concurrency (8 by default) and returns a structured result that keeps successes and failures separate, in input order — so one bad key never sinks the whole batch.

// Upload several objects in one call
const { uploaded, errors } = await files.upload([
  { key: "a.txt", body: "alpha" },
  { key: "b.txt", body: "beta", contentType: "text/plain" },
]);

// exists() splits the keys into present and absent
const { existing, missing } = await files.exists(["a.txt", "b.txt", "c.txt"]);

// delete() reports what it removed
const { deleted } = await files.delete(["a.txt", "b.txt"]);

Each method returns a result shaped for what it does, and every one carries an optional errors array — omitted entirely when every item succeeded:

MethodArray form returns
upload{ uploaded, errors? }
download{ downloaded, errors? }
head{ files, errors? }
exists{ existing, missing, errors? }
delete{ deleted, errors? }

The success arrays come back in the order you supplied the keys. Each entry in errors is { key, error }, where error is a normalized FilesError. Invalid keys (empty, or containing null bytes) are reported there too, never thrown.

Partial failure

By default the array forms don't throw on a partial failure — every item is attempted and per-key failures collect in errors. Pass stopOnError: true to bail at the first failure and return the results gathered so far plus that error (this path runs sequentially), or concurrency to tune the fan-out.

const result = await files.upload(items, {
  concurrency: 16,
  stopOnError: false,
});

if (result.errors) {
  for (const { key, error } of result.errors) {
    logger.warn("upload failed", key, error.code);
  }
}

Prop

Type

Native bulk vs. fan-out

upload, download, head, and exists have no provider batch primitive, so the SDK always fans out to per-key calls under the concurrency limit. delete is the exception: adapters with a native bulk primitive (S3 DeleteObjects, chunked into batches of 1000; Supabase; UploadThing) remove everything in a single request and ignore concurrency, while the rest fall back to bounded fan-out. When a native bulk provider only reports that the whole request failed, that error is mapped onto each affected key.

Retries and hooks

Bulk calls are not retried — retries applies to single-operation calls only — so onRetry never fires for them. onAction emits one event for the whole call, carrying the caller's keys and the aggregated result; the per-item failures inside errors are not rejections, so they don't fire onError.

The client's prefix is honored throughout: keys are resolved under it on the way in and stripped back off on the way out, just as in the single-key forms.

On this page