FAQ

Common questions about providers, bundling, switching backends, browser usage, errors, and the CLI.

Which providers are supported?

40+, behind one API: AWS S3 and the whole S3-compatible long tail (R2, MinIO, Backblaze, Wasabi, Scaleway, OVH, Hetzner, Tigris, Storj, Filebase, Akamai, IDrive, Vultr, IBM COS, Oracle, Exoscale, DigitalOcean Spaces), Vercel Blob, Google Cloud Storage, Azure, Supabase, the consumer providers (Dropbox, Box, Google Drive, OneDrive, SharePoint), upload-focused services (UploadThing, Cloudinary), the BaaS stack (Appwrite, PocketBase, Firebase Storage), and a local fs adapter for tests. See the full list under Adapters.

Do I have to install every provider's SDK?

No. Adapters are subpath exports (files-sdk/s3, files-sdk/r2, ...) and each provider SDK is an optional peer dependency, loaded lazily on first use. Install only the one(s) you wire up - the SDK you don't import is never bundled. See Installation.

Why am I getting "Cannot find module '@aws-sdk/client-s3'"?

Provider SDKs are peer dependencies - install only the ones for the adapters you use. The per-adapter docs list the exact packages.

How do I switch providers?

Swap the adapter you pass to new Files({ adapter }). Every call site below the constructor stays identical - the code that uploads to S3 is the code that uploads to Vercel Blob. See Usage.

What happens when a provider doesn't support a feature?

Every method normalizes to the same shape, but providers differ in what they offer natively. Where a provider lacks a primitive, the method throws a FilesError rather than silently misbehaving (for example, signedUploadUrl on Vercel Blob). Each adapter's docs page has a Compatibility section listing its per-method support, and files.raw is the typed escape hatch for anything outside the unified surface.

Can I use it in the browser?

The core Files class and the StoredFile type are isomorphic, but most adapters wrap server-only SDKs. For browser uploads, mint a contract with signedUploadUrl() on the server and have the client fetch directly to the provider. See Troubleshooting for the full pattern.

How are errors handled?

Every method throws a single FilesError with a normalized code - NotFound, Unauthorized, Conflict, or Provider - and the original provider error attached as cause. See Errors.

Does delete throw on a missing key?

Depends on the provider. S3, R2, and Vercel Blob treat delete as idempotent and resolve successfully; strict providers throw a FilesError with code: "NotFound". If you need consistent behavior, gate on await files.exists(key) first. See delete.

Why is contentType application/octet-stream?

When upload can't infer the type from the input (a raw ArrayBuffer, a ReadableStream, or a string with no extension hint), it defaults to application/octet-stream. Pass contentType explicitly to override it.

Why does the body come back empty from head / list?

Body accessors on head and list results are lazy - they fetch on call. If you serialize a StoredFile without invoking an accessor, you get the metadata only. That's the intent; call .arrayBuffer() / .text() / .blob() / .stream() to materialize the bytes. See Troubleshooting for the lazy-body gotcha.

Is there a CLI?

Yes - a files binary with JSON-by-default output, stdin/stdout streaming, and a built-in MCP server, with the same semantics as the SDK. See the CLI docs.

On this page