Skip to content

Declarative exports creates Durable Object classes that are not container-enabled (DURABLE_OBJECT_NOT_CONTAINER_ENABLED), with no recovery path #14614

Description

@jonastemplestein

Note: this issue was researched, reproduced, and written by an AI agent (Claude Code) operating the account of a human maintainer of our codebase, who reviewed it before filing. Happy to provide account/script details privately.

Summary

Durable Object classes created by the new declarative exports reconciliation (GA in wrangler 4.107.0) are not container-enabled: the containers phase of the same deploy fails with DURABLE_OBJECT_NOT_CONTAINER_ENABLED, permanently. Because the exports flow is one-way (API error 100403 forbids reverting to migrations) and there is no API to container-enable an existing namespace, there is no way to create a containerized DO class on an exports-mode Worker — including any brand-new Worker whose config uses exports. The only escape we found is deleting the entire Worker and recreating the class via a legacy-migrations deploy, losing all DO data on the Worker.

The upload metadata does include containers: [{class_name}] alongside exports (same makeWorkerUploadFormData codepath as legacy), so this looks like the server-side exports reconciliation ignoring the sibling containers metadata when creating classes. Consistent with that, packages/wrangler/e2e/durable-objects-exports.test.ts has no container coverage.

Reproduction (verified as written, wrangler 4.107.0)

Three files in an empty directory:

worker.js

import { DurableObject } from "cloudflare:workers";
export class MyContainerDO extends DurableObject {}
export default { fetch: () => new Response("ok") };

Dockerfile

FROM alpine:latest
CMD ["sleep", "infinity"]

wrangler.json

{
  "name": "exports-containers-repro",
  "main": "worker.js",
  "compatibility_date": "2026-07-01",
  "durable_objects": { "bindings": [{ "name": "MY_DO", "class_name": "MyContainerDO" }] },
  "exports": { "MyContainerDO": { "type": "durable-object", "storage": "sqlite" } },
  "containers": [{ "class_name": "MyContainerDO", "image": "./Dockerfile", "max_instances": 1 }]
}

Step 1npx wrangler@4.107.0 deploy (Worker does not exist yet). The script upload succeeds and reconciliation creates the class, then the containers phase fails:

Durable Object exports reconciliation:
  Created: MyContainerDO
Uploaded exports-containers-repro (2.79 sec)
├ NEW exports-containers-repro-mycontainerdo
✘ [ERROR] Error creating application due to a misconfiguration:
  DURABLE_OBJECT_NOT_CONTAINER_ENABLED

Step 2 — run npx wrangler@4.107.0 deploy again: identical failure. The namespace never becomes container-enabled.

Step 3 — attempt recovery by replacing the exports key with "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyContainerDO"] }] and deploying:

✘ [ERROR] A request to the Cloudflare API (/accounts/…/workers/scripts/exports-containers-repro) failed.
  This Worker was last deployed using the declarative `exports` flow; reverting to `migrations` is not supported. Please continue to use `exports`. [code: 100403]

Step 4 (control) — the exact same three files, but with the migrations array instead of exports and a fresh Worker name, deploy cleanly: the class is created container-enabled and the application is created (├ NEW migrations-containers-control-mycontainerdo, no error).

We have also verified the transition direction works: a class created via migrations (container-enabled) keeps working after the config switches to exports — the gap is only in class creation under exports.

Expected

Classes created by exports reconciliation should honor the upload's containers metadata (or exports entries should be able to declare container-enablement), so containerized classes — and brand-new containerized Workers — are creatable under the declarative flow. Alternatively: an API to container-enable an existing namespace, or permitting the migrations transition for repair.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions