Durable Object class exports·续1

   {
     "exports": {
       "MyDO": {
         "type": "durable-object",
         "state": "expecting-transfer",
         "storage": "sqlite",
         "transfer_from": "source-worker"
       }
     }
   }
   ```

```toml
   [exports.MyDO]
   type = "durable-object"
   state = "expecting-transfer"
   storage = "sqlite"
   transfer_from = "source-worker"
   ```

Deploy the target. Cloudflare records a pending transfer and emits a `Transfer pending` notice in the [reconciliation output](#reading-the-reconciliation-output).
3. **Source Worker — commit the transfer.** Change the source's `MyDO` entry to a `transferred` tombstone naming the target Worker. Leave the class in the source code for now:

```jsonc
   {
     "exports": {
       "MyDO": {
         "type": "durable-object",
         "state": "transferred",
         "transferred_to": "target-worker"
       }
     }
   }
   ```

```toml
   [exports.MyDO]
   type = "durable-object"
   state = "transferred"
   transferred_to = "target-worker"
   ```

Deploy the source. Cloudflare matches the pending transfer and atomically reassigns the namespace to the target Worker. The reconciliation output reports `Transferred (committed): MyDO → target-worker`.

If the source Worker still needs to access `MyDO` after the transfer commits, update its `durable_objects.bindings` entry to point at the target Worker with `script_name`:

```jsonc
   {
     "durable_objects": {
       "bindings": [
         {
           "name": "MY_DURABLE_OBJECT",
           "class_name": "MyDO",
           "script_name": "target-worker"
         }
       ]
     }
   }
   ```

```toml
   [[durable_objects.bindings]]
   name = "MY_DURABLE_OBJECT"
   class_name = "MyDO"
   script_name = "target-worker"
   ```

If the source Worker no longer needs to access `MyDO`, remove the binding when you remove `MyDO` from the source Worker's code.
4. **Target Worker — bind the class.** Once the source's deploy has fully rolled out, redeploy the target with a `durable_objects.bindings` entry for `MyDO`. The binding now resolves to the namespace on the target Worker.

```jsonc
   {
     "durable_objects": {
       "bindings": [
         {
           "name": "MY_DURABLE_OBJECT",
           "class_name": "MyDO"
         }
       ]
     },
     "exports": {
       "MyDO": { "type": "durable-object", "storage": "sqlite" }
     }
   }
   ```

```toml
   [[durable_objects.bindings]]
   name = "MY_DURABLE_OBJECT"
   class_name = "MyDO"

[exports.MyDO]
   type = "durable-object"
   storage = "sqlite"
   ```

After the handoff has rolled out everywhere, you can remove `MyDO` from the source Worker's code and delete the `transferred` tombstone from the source's `exports` map. While other Workers in the account still bind to `MyDO` on the source, the reconciliation output lists them in `referencing_scripts`; redeploy each of them with bindings re-pointed at the target before removing the source tombstone.

### Cancel a pending transfer

A pending transfer persists until the source Worker commits with a `transferred` tombstone, or the target Worker cancels by removing the `expecting-transfer` entry. To cancel, redeploy the target without the entry (or replace it with a normal live entry). Cloudflare deletes the pending record and the source Worker keeps its namespace.

### Transfer constraints

- Both Workers must live in the same Cloudflare account.
- Cross- [dispatch-namespace](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/) transfers are not supported. The source and target Workers must both be in the same dispatch-namespace context (or both be outside any dispatch namespace).
- A target Worker can hold only one pending phase-1 hint per class at a time. To redirect a pending transfer to a different source, cancel the current pending transfer first.

## Storage backends

Live entries (`state: "created"` and `state: "expecting-transfer"`) must declare a `storage` value:

- `"sqlite"` selects the SQLite storage backend. This is the recommended and only path for new namespaces. SQLite-backed namespaces support [SQL](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/), [Point-in-Time Recovery](https://developers.cloudflare.com/durable-objects/api/sqlite-storage-api/#pitr-point-in-time-recovery-api), and a higher per-object storage limit.
- `"legacy-kv"` selects the key-value storage backend. Cloudflare only accepts this value for namespaces that were already provisioned with key-value storage (typically Workers that started on the [legacy `migrations` array](https://developers.cloudflare.com/durable-objects/reference/durable-object-class-migrations-legacy/) before SQLite was the default). You cannot create a **new** key-value-backed namespace through `exports`.

Note

Durable Objects are available both on Workers Free and Workers Paid plans.

- **Workers Free plan**: Only Durable Objects with [SQLite storage backend](https://developers.cloudflare.com/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) are available.
- **Workers Paid plan**: Durable Objects with the SQLite storage backend are available. The [key-value storage backend](https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/#storage-backends) is only available to accounts that already have a key-value-backed namespace.

If you wish to downgrade from a Workers Paid plan to a Workers Free plan, you must first ensure that you have deleted all Durable Object namespaces with the key-value storage backend.

Storage type is immutable once a namespace exists. Cloudflare rejects an `exports` change that switches a provisioned namespace from `sqlite` to `legacy-kv` or vice versa with the error `storage_type_mismatch`. To genuinely change storage backends, delete the namespace via a `deleted` tombstone and re-provision it under the new backend (with full data loss in between).

## Reading the reconciliation output

When a deployment applies a Durable Object class lifecycle change, `wrangler deploy` prints a `Durable Object exports reconciliation` block. The block also appears when reconciliation produces warnings, info notices, or removable entries. Wrangler omits the block when nothing changed and there are no notices.

A typical output looks like:

```txt
Durable Object exports reconciliation:
  Created: ChatRoom
  Renamed: OldRoom → ChatRoom
  Transferred (committed): Widget → target-worker
  Transfer pending: Incoming ← source-worker

Info:
    [tombstone_class_still_in_code] OldRoom: Tombstone of type 'renamed' applied. Class 'OldRoom' is still exported in code; this is the supported pattern for zero-downtime rename rollouts.
    [stale_tombstone] OldGone: Tombstone of type 'deleted' for class 'OldGone' has no effect (no namespace exists with this class name). Safe to remove from `exports`.

Safe to remove from `exports`: OldGone

The block has four sections:

  • Action lines ( Created, Updated, Deleted, Renamed, Transferred, Transfer pending) report the changes Cloudflare applied during this deploy.
  • Warnings (yellow) flag conditions that should be investigated but do not block the deploy. The set of warning scenarios is reserved for future use; current Cloudflare deploys do not emit them.
  • Info (dimmed) reports non-blocking notices: stale tombstones that no longer apply, and tombstones applied while the source class is still in code (the supported pattern for zero-downtime rollouts).
  • Safe to remove from exports lists tombstone entries that are stale and have no other Workers in the account binding to the source class. You can delete these entries from your exports map on your next config edit.

Info entries can include a referencing_scripts list — other Workers in the account whose bindings still resolve to the affected namespace. Redeploy those Workers with bindings re-pointed at the new class name before removing the tombstone, or you will orphan their bindings.

If the deploy fails, the output shows a structured error per class:

✘ [orphaned_provisioned_namespace] class 'Bar': A namespace exists for 'Bar' but no `exports` entry declares it.
    Suggestion: add an `exports` entry for 'Bar', or add a `deleted` tombstone to remove the namespace.
    Referencing scripts: worker-foo, worker-bar

All class-level errors from a single deploy are reported together so you can fix them in one round trip. Refer to the error reference for the full list of error scenarios.

Stale tombstones and cleanup

A tombstone becomes stale when applying it produces no state change — for example, a deleted tombstone for a class whose namespace has already been removed, or a renamed tombstone after the rename has already landed.

Cloudflare emits a stale_tombstone info notice on every deploy where a tombstone is stale, until you remove the entry from exports. The notice is intentionally repeated so you do not forget to clean up.

For renamed and transferred tombstones, Cloudflare also enumerates other Workers in your account whose durable_objects.bindings entries still reference the source class name. These appear as referencing_scripts on the info notice. While referencing_scripts is non-empty, removing the tombstone could orphan those bindings — redeploy the referencing Workers with bindings re-pointed at the new class first.

The top-level "Safe to remove from exports" line in the reconciliation output names every stale tombstone whose referencing_scripts is empty. Use that list as the authoritative "you can delete these now" hint.

Environments, dispatch namespaces, and previews

exports can be specified at the top level of your Wrangler configuration file and overridden per environment:

{
    // top-level exports apply to the default environment
    "exports": {
        "MyDO": { "type": "durable-object", "storage": "sqlite" }
    },
    "env": {
        "staging": {
            // override for the staging environment
            "exports": {
                "MyDO": { "type": "durable-object", "storage": "sqlite" }
            }
        }
    }
}
[exports.MyDO]
type = "durable-object"
storage = "sqlite"

[env.staging.exports.MyDO]
type = "durable-object"
storage = "sqlite"

If exports is only declared at the top level, named environments inherit the same value. Each environment maintains its own provisioned namespaces, so tombstones apply only within the environment they are declared in.

Previews and dispatch namespaces follow the same rules. Cross-dispatch-namespace transfers are not supported — the source and target Workers in a transferred / expecting-transfer pair must both live in the same dispatch-namespace context (or both be outside any dispatch namespace).

exports configuration reference

The exports field is a map keyed by Durable Object class name. Each value is an object whose fields depend on state:

  • type string required
  • For Durable Object class entries, set this to "durable-object".
  • state string optional
  • The lifecycle state. One of "created" (the default, omit to use), "deleted", "renamed", "transferred", or "expecting-transfer".
  • storage string conditional
  • Required when state is "created" or "expecting-transfer". One of "sqlite" (recommended, the only valid value for new namespaces) or "legacy-kv" (only for existing key-value-backed namespaces). Forbidden on tombstone states.
  • renamed_to string conditional
  • Required when state is "renamed". The destination class name. Must be a valid JavaScript identifier, differ from the source class name, and appear as a live entry in the same exports map.
  • transferred_to string conditional
  • Required when state is "transferred". The name of the target Worker that will receive the namespace.
  • transfer_from string conditional
  • Required when state is "expecting-transfer". The name of the source Worker the namespace is being transferred from.