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
Step 1 — npx 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.
Summary
Durable Object classes created by the new declarative
exportsreconciliation (GA in wrangler 4.107.0) are not container-enabled: the containers phase of the same deploy fails withDURABLE_OBJECT_NOT_CONTAINER_ENABLED, permanently. Because the exports flow is one-way (API error 100403 forbids reverting tomigrations) 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 usesexports. The only escape we found is deleting the entire Worker and recreating the class via a legacy-migrationsdeploy, losing all DO data on the Worker.The upload metadata does include
containers: [{class_name}]alongsideexports(samemakeWorkerUploadFormDatacodepath as legacy), so this looks like the server-side exports reconciliation ignoring the siblingcontainersmetadata when creating classes. Consistent with that,packages/wrangler/e2e/durable-objects-exports.test.tshas no container coverage.Reproduction (verified as written, wrangler 4.107.0)
Three files in an empty directory:
worker.jsDockerfilewrangler.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 1 —
npx wrangler@4.107.0 deploy(Worker does not exist yet). The script upload succeeds and reconciliation creates the class, then the containers phase fails:Step 2 — run
npx wrangler@4.107.0 deployagain: identical failure. The namespace never becomes container-enabled.Step 3 — attempt recovery by replacing the
exportskey with"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyContainerDO"] }]and deploying:Step 4 (control) — the exact same three files, but with the
migrationsarray instead ofexportsand 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 toexports— the gap is only in class creation under exports.Expected
Classes created by exports reconciliation should honor the upload's
containersmetadata (orexportsentries 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.