Skip to content

[wrangler,miniflare,vite-plugin] Add Durable Object retry policies - #15872

Closed
apeacock1991 wants to merge 3 commits into
mainfrom
apeacock/STOR-5615-do-retry-userland
Closed

apeacock1991 wants to merge 3 commits into
mainfrom
apeacock/STOR-5615-do-retry-userland

Conversation

@apeacock1991

@apeacock1991 apeacock1991 commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

This is the workers-sdk side of Durable Object retry controls. The Worker that exports a Durable Object class sets its retry policy, and the runtime applies it however the class is reached: env, ctx.exports, or a binding from another Worker. Callers can't override it.

{
	"exports": {
		"Counter": {
			"type": "durable-object",
			"storage": "sqlite",
			"retry": { "max_attempts": 5, "timeout_ms": 10000 },
		},
	},
}

max_attempts counts retries after the first request, so 5 allows six attempts in total. Zero turns retries off. timeout_ms is a retry timeout, not a request timeout. The first request always runs to completion, and no retry starts once the timeout has passed. Anything omitted uses the runtime defaults of 4 retries and 10,000 ms. Neither setting makes a call retryable if the runtime wouldn't already retry it.

Workers still on migrations can't use exports, so they can set retry on a binding to a class in the same Worker. That's Wrangler config only. cloudflare.config.ts has it on exports and nowhere else. Validation rejects:

  • retry on a binding with script_name
  • retry on both a binding and the class's export
  • two same-Worker bindings to one class with different values

A method opts into retries that may run it twice with retryable() from the cloudflare:durable-objects runtime module. The runtime side is cloudflare/workerd#7617, and moving the policy onto the namespace is a separate workerd PR.

It's easiest to review one commit at a time.

  1. Config, validation and upload. Wrangler config types, validation, and the generated JSON schema, plus retry on live exports in the experimental config API. Exports already upload as written, so exports.<Class>.retry goes out as { max_attempts, timeout_ms }. Same-Worker bindings keep sending retry in the same shape. Wrangler never fills in defaults.
  2. Local dev. Miniflare writes the policy to workerd's DurableObjectNamespace.retryPolicy rather than to each binding. Wrangler dev builds a class-name-to-policy map from exports and same-Worker bindings, the same way it handles useSQLite. Miniflare's retryMaxAttempts/retryTimeoutMs options now configure the class, so they're rejected alongside scriptName.
  3. Vite plugin. cloudflare:durable-objects joins the plugin's list of built-in runtime modules, so Vite leaves the import for workerd instead of resolving it as a package. Wrangler already treats every cloudflare:* import as external.

The generated Cap'n Proto types in miniflare come from the workerd PR's workerd.capnp, because no workerd release includes the namespace field yet. The bump will regenerate them. Until then the installed workerd ignores the field, so the local retry policy has no effect, and the playground test for cloudflare:durable-objects stays red.

Known gaps:

  • When the dev registry sends a binding to a Worker in another wrangler dev process, the call goes through the proxy's own namespace and uses the default policy, not the owner's.
  • Under vite dev and the Vitest plugin, Durable Objects run behind wrapper classes, so the runtime won't see the retryable() mark. The methods still run normally.

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: public docs will ship when the runtime starts applying these settings. Until then the settings have no effect.

ask-bonk[bot]

This comment was marked as resolved.

@ask-bonk

ask-bonk Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

Adds Durable Object retry policies and Worker decorator lowering.

  1. P1 Posted one inline suggestion: the decorator pre-check misses valid decorators after URL strings on the same line.

github run

@apeacock1991 apeacock1991 changed the title [wrangler,miniflare,vite-plugin] Add Durable Object binding retry policies and decorator support [wrangler,miniflare,vite-plugin,vitest-plugin] Add Durable Object binding retry policies and decorator support Sep 25, 2026
@changeset-bot

changeset-bot Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dff1555

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 15 packages
Name Type
wrangler Minor
miniflare Minor
@cloudflare/config Minor
@cloudflare/workers-utils Minor
@cloudflare/deploy-helpers Minor
@cloudflare/vite-plugin Patch
@cloudflare/vitest-plugin Patch
@cloudflare/pages-shared Patch
@cloudflare/remote-bindings Patch
@cloudflare/runtime-types Patch
@cloudflare/autoconfig Patch
@cloudflare/build-output-utils Patch
@cloudflare/containers-shared Patch
@cloudflare/cli-shared-helpers Patch
@cloudflare/workers-auth Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch 3 times, most recently from b0ee729 to bf0aba3 Compare September 25, 2026 16:22
@pkg-pr-new

pkg-pr-new Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@15872

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@15872

@cloudflare/codemods

npm i https://pkg.pr.new/@cloudflare/codemods@15872

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@15872

@cloudflare/containers-shared

npm i https://pkg.pr.new/@cloudflare/containers-shared@15872

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@15872

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@15872

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@15872

miniflare

npm i https://pkg.pr.new/miniflare@15872

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@15872

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@15872

@cloudflare/runtime-types

npm i https://pkg.pr.new/@cloudflare/runtime-types@15872

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@15872

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@15872

@cloudflare/vitest-plugin

npm i https://pkg.pr.new/@cloudflare/vitest-plugin@15872

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@15872

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@15872

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@15872

wrangler

npm i https://pkg.pr.new/wrangler@15872

commit: dff1555

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from bf0aba3 to 284c689 Compare September 25, 2026 22:50
@apeacock1991
apeacock1991 marked this pull request as ready for review September 29, 2026 10:08
@workers-devprod
workers-devprod requested review from a team and cjol and removed request for a team September 29, 2026 10:09
@workers-devprod

workers-devprod commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/cloudflare-durable-objects-module.md: [@cloudflare/wrangler]
  • .changeset/durable-object-binding-retry-local.md: [@cloudflare/wrangler]
  • .changeset/durable-object-binding-retry.md: [@cloudflare/wrangler]
  • .changeset/vitest-plugin-decorators.md: [@cloudflare/wrangler]
  • fixtures/vitest-plugin-examples/durable-objects/src/index.ts: [@cloudflare/wrangler]
  • fixtures/vitest-plugin-examples/durable-objects/src/positive.ts: [@cloudflare/wrangler]
  • fixtures/vitest-plugin-examples/durable-objects/test/decorators.test.ts: [@cloudflare/wrangler]
  • packages/config/src/tests/convert.test.ts: [@cloudflare/wrangler]
  • packages/config/src/tests/schema.test.ts: [@cloudflare/wrangler]
  • packages/config/src/bindings.ts: [@cloudflare/wrangler]
  • packages/config/src/convert.ts: [@cloudflare/wrangler]
  • packages/config/src/schema.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/deploy/helpers/create-worker-upload-form.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/preview/api.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/preview/shared.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/config/v4-convert.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/config/v4-schema.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/index.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/plugins/do/index.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/runtime/config/workerd.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/config/v4-convert.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/config/v4-schema.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/plugins/do/index.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/runtime/config.spec.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/package.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/tests/decorators.spec.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/package.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/package/index.js: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/package/package.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/src/index.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/src/logged.js: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/tsconfig.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/tsconfig.node.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/tsconfig.worker.json: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/vite.config.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/decorators/wrangler.jsonc: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/scripts/deps.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/cloudflare-environment.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/index.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/plugins/decorators.ts: [@cloudflare/wrangler]
  • packages/vitest-plugin/src/pool/plugin.ts: [@cloudflare/wrangler]
  • packages/workers-utils/AGENTS.md: [@cloudflare/wrangler]
  • packages/workers-utils/package.json: [@cloudflare/wrangler]
  • packages/workers-utils/scripts/deps.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/environment.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/validation.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/decorators.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/map-worker-metadata-bindings.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/types.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/worker.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/decorators.test.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/map-worker-metadata-bindings.test.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tsup.config.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/config-schema.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/config/configuration.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/create-worker-upload-form/bindings.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/deploy/bindings.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/deploy/build.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/preview.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/unstable-get-miniflare-worker-options.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/versions/versions.upload.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/dev/miniflare/index.ts: [@cloudflare/wrangler]
  • pnpm-lock.yaml: [@cloudflare/wrangler]

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from 284c689 to f5911a0 Compare September 29, 2026 10:16
devin-ai-integration[bot]

This comment was marked as resolved.

@jamesopstad jamesopstad left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a blocking review until this has been discussed more. I don't we should be adding decorators support to the Vite plugin. There are good reasons that they are not supported in Oxc and they were recently moved back to Stage 2.7.

Additionally, v2 of the Vite plugin (currently in beta) doesn't have a dependency on Wrangler so ESBuild would be a new dependency.

@apeacock1991

Copy link
Copy Markdown
Contributor Author

Adding a blocking review until this has been discussed more. I don't we should be adding decorators support to the Vite plugin. There are good reasons that they are not supported in Oxc and they were recently moved back to Stage 2.7.

Additionally, v2 of the Vite plugin (currently in beta) doesn't have a dependency on Wrangler so ESBuild would be a new dependency.

Do you have any suggested alternatives that are also nice from a DX perspective? e.g. this needs to live in the code, on the method it relates to. I went through a lot of options and this came out as the best one, it's also already used in Cap'n stuff I believe so there is precedence for it.

For this to work, the runtime needs to know at construction what methods are retryable and what are not, in terms of requirements.

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from f5911a0 to f6ac70f Compare October 5, 2026 10:00
@workers-devprod

workers-devprod commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/durable-object-retry-policy.md: [@cloudflare/wrangler]
  • .changeset/vite-plugin-durable-objects-module.md: [@cloudflare/wrangler]
  • packages/config/src/tests/convert.test.ts: [@cloudflare/wrangler]
  • packages/config/src/tests/schema.test.ts: [@cloudflare/wrangler]
  • packages/config/src/convert.ts: [@cloudflare/wrangler]
  • packages/config/src/exports.ts: [@cloudflare/wrangler]
  • packages/config/src/schema.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/deploy/helpers/create-worker-upload-form.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/preview/api.ts: [@cloudflare/wrangler]
  • packages/deploy-helpers/src/preview/shared.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/config/v4-convert.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/config/v4-schema.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/index.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/plugins/do/namespaces.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/plugins/shared/index.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/runtime/config/generated/workerd.ts: [@cloudflare/wrangler]
  • packages/miniflare/src/runtime/config/workerd.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/config/v4-convert.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/config/v4-schema.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/plugins/do/index.spec.ts: [@cloudflare/wrangler]
  • packages/miniflare/test/runtime/config.spec.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/module-resolution/tests/base-tests.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/playground/module-resolution/src/cloudflare-imports.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/cloudflare-environment.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/environment.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/config/validation.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/map-worker-metadata-bindings.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/types.ts: [@cloudflare/wrangler]
  • packages/workers-utils/src/worker.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts: [@cloudflare/wrangler]
  • packages/workers-utils/tests/map-worker-metadata-bindings.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/config-schema.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/config/configuration.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/create-worker-upload-form/bindings.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/deploy/bindings.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/deploy/durable-objects.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/preview.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/unstable-get-miniflare-worker-options.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/tests/versions/versions.upload.test.ts: [@cloudflare/wrangler]
  • packages/wrangler/src/dev/miniflare/index.ts: [@cloudflare/wrangler]

devin-ai-integration[bot]

This comment was marked as resolved.

@apeacock1991 apeacock1991 changed the title [wrangler,miniflare,vite-plugin,vitest-plugin] Add Durable Object binding retry policies and decorator support [wrangler,miniflare] Add Durable Object binding retry policies Oct 5, 2026
@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch 2 times, most recently from f884c64 to 011b510 Compare October 5, 2026 11:20
@apeacock1991

Copy link
Copy Markdown
Contributor Author

For completeness: after a lengthy discussion, the decision was made to use a directive ("use retry") rather than a decorator, as the latter needed explicit changes to the bundler to make it supported.

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from 011b510 to 5731b1b Compare October 6, 2026 09:54
@apeacock1991 apeacock1991 changed the title [wrangler,miniflare] Add Durable Object binding retry policies [wrangler,miniflare,vite-plugin] Add Durable Object binding retry policies Oct 6, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from 5731b1b to 70d863c Compare October 6, 2026 10:22

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 0 new potential issues.

Devin Review

@jamesopstad
jamesopstad removed the request for review from cjol October 9, 2026 11:10
@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from 70d863c to d87708b Compare October 9, 2026 13:13
@apeacock1991 apeacock1991 changed the title [wrangler,miniflare,vite-plugin] Add Durable Object binding retry policies [wrangler,miniflare,vite-plugin] Add Durable Object retry policies Oct 9, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from d87708b to b73cacf Compare October 9, 2026 14:36
devin-ai-integration[bot]

This comment was marked as resolved.

The Worker that exports a Durable Object class owns its retry policy.
Live `exports` entries accept an optional `retry` object, and Workers
that use `migrations` can set it on a binding to a class in the same
Worker. `max_attempts` is the number of retries after the first request,
from 0 to 10. `timeout_ms` is the retry timeout, from 500 to 60,000.
Unknown keys are errors, the same as in the Workers API.

Validation rejects `retry` on a binding with `script_name`, on any
binding in a Worker that declares Durable Objects in `exports`, and on
two bindings to one class with different values. The policy applies to
every caller, so a caller cannot set its own.

Wrangler sends the policy as written with the export or binding in
deploy, versions upload and preview metadata, and reads binding
policies back when it downloads remote config. It never fills in
defaults. The runtime owns those.

The experimental config API accepts `retry: { maxAttempts, timeoutMs }`
on live `durable-object` exports and converts it to Wrangler's shape.
Bindings there have no `retry`.
workerd now reads the retry policy from the Durable Object namespace
instead of each binding, so `env`, `ctx.exports` and bindings from other
Workers all use the class owner's policy. Miniflare carries `retry` on
live durable-object exports and writes it to
`DurableObjectNamespace.retryPolicy`. Ephemeral namespaces get no
policy, because workerd does not support one there.

Miniflare's `durableObjects` and `additionalUnboundDurableObjects`
entries accept `retryMaxAttempts` and `retryTimeoutMs`. They configure
the class, so an entry with `scriptName` cannot set them, and entries
for one class with different values are an error.

Wrangler dev builds a class-name-to-policy map from exports and
same-Worker bindings and applies it to every local entry for the class.

The generated Cap'n Proto types come from the workerd change that moves
the policy. The binding field becomes an unused placeholder. Regenerate
them when the workerd version is bumped.
Durable Object methods are marked as safe to retry with `retryable()`
from the `cloudflare:durable-objects` runtime module. Add it to the
plugin's built-in module list so Vite leaves the import for workerd
instead of resolving it as a package. Wrangler already treats every
`cloudflare:*` import as external.
@apeacock1991
apeacock1991 force-pushed the apeacock/STOR-5615-do-retry-userland branch from b73cacf to dff1555 Compare October 9, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants