---
title: FAQ
description: Common questions about supported providers, bundling and peer dependencies, switching backends, browser uploads, error handling, 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](/adapters/s3).

## 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](/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](/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](/adapters/vercel-blob) has a Compatibility section listing its per-method support, and `files.raw` is the [typed escape hatch](/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()`](/api/signed-upload-url) on the server and have the client `fetch` directly to the provider. See [Troubleshooting](/troubleshooting) for the full pattern.

## How are errors handled?

Every method throws a single `FilesError` with a normalized `code` - `NotFound`, `Unauthorized`, `Conflict`, `ReadOnly`, or `Provider` - and the original provider error attached as `cause`. See [Errors](/api/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](/api/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](/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](/cli) docs.
