---
title: Escape hatch
description: Drop down to the native, per-adapter client for any feature outside the unified surface — versioning, lifecycle rules, ACLs, object tags, and more.
---

The unified surface is deliberately small — the methods every adapter can implement. When you need a provider feature that isn't part of it (S3 versioning, lifecycle rules, ACLs, object tags, anything), drop down to the native client instead of waiting for it to be wrapped.

## `files.raw`

`raw` is the underlying provider client, typed per adapter — `S3Client` for `s3()`, `VercelBlobClient` for `vercelBlob()`, an `R2Bucket` (or `S3Client` in HTTP mode) for `r2()`, and so on. The type flows through from the adapter, so you keep full autocomplete on it.

```ts lineNumbers
const files = new Files({ adapter: s3({ bucket: "uploads" }) });

// `files.raw` is typed as S3Client — no cast needed.
await files.raw.send(
  new PutObjectAclCommand({
    Bucket: "uploads",
    Key: "a.png",
    ACL: "public-read",
  })
);
```

`files.adapter` is the adapter itself, if you need its `name` or want to pass it around; `files.adapter.raw` is the same client as `files.raw`.

## What `raw` bypasses

The native client talks to the provider directly, so none of the `Files` wrapper's behavior applies to calls you make through it:

- **No prefix scoping.** You pass the provider's full key yourself, including any `prefix` the `Files` instance was constructed with — `files.raw` doesn't know about it.
- **No normalized errors.** A failure throws the provider's own error type, not a [`FilesError`](/api/errors); catch and classify accordingly.
- **No hooks, retries, timeouts, or cancellation.** [Hooks](/usage#hooks) don't fire, and `signal` / `timeout` / `retries` aren't applied. Use the provider client's own equivalents.

In short, `raw` is an unmanaged door straight to the provider. Reach for it for the one feature you need, and keep using the unified methods for everything else.

## Keeping the type

The `raw` type is inferred from the adapter you pass in, so it stays typed as long as the instance keeps its concrete type. Widening to the bare `Files` (whose adapter is the default `Adapter`) erases it:

```ts lineNumbers
const files = new Files({ adapter: s3({ bucket: "uploads" }) });
files.raw; // S3Client

const widened: Files = files;
widened.raw; // unknown
```

If you store a `Files` instance on a typed field, annotate it with the adapter's type (for example `Files<S3Adapter>`) rather than the bare `Files`, and `raw` stays typed.

See the Compatibility section on each [adapter's docs page](/adapters/s3) for what it supports through the unified surface before reaching for the escape hatch.
