Skip to content

[config] Improve durableObject export error for invalid state properties#14648

Open
MattieTK wants to merge 1 commit into
mainfrom
fix/durable-object-export-error-message
Open

[config] Improve durableObject export error for invalid state properties#14648
MattieTK wants to merge 1 commit into
mainfrom
fix/durable-object-export-error-message

Conversation

@MattieTK

@MattieTK MattieTK commented Jul 10, 2026

Copy link
Copy Markdown
Member

Describe your change...

The exports.durableObject() factory in @cloudflare/config uses five overloads, one per Durable Object state (created, deleted, renamed, transferred, expecting-transfer), each with a precise return type that downstream type inference (e.g. InferDurableNamespaces) relies on to distinguish live namespaces from tombstones. The problem is that when a user writes something TypeScript can't match to any overload – for example:

exports: {
  NewCounter: exports.durableObject({ storage: "sqlite" }),
  Counter: exports.durableObject({ storage: "sqlite", state: "renamed", renamedTo: "NewCounter" }),
}

where storage is invalid on a renamed entry – TypeScript falls back to reporting the last overload's error, which happens to be expecting-transfer. So instead of telling you to remove storage, it says:

Type '"renamed"' is not assignable to type '"expecting-transfer"'.

This sends you down the wrong path: expecting-transfer is the state that accepts a storage object, but the real problem is that storage shouldn't be present on a renamed entry at all.

This PR adds one extra fallback overload at the end whose parameter is typed as the full discriminated union (DurableObjectExportOptions) rather than a single state. When the five precise overloads all fail, TypeScript now falls back to this union overload, and because the parameter is a discriminated union it performs a discriminant-aware excess-property check: it reads state: "renamed", narrows to DurableObjectRenamedExportOptions, and reports the offending property directly:

No overload matches this call.
  The last overload gave the following error.
    Object literal may only specify known properties, and 'storage' does not exist in type 'DurableObjectRenamedExportOptions'.

The same improvement applies to the other states (e.g. storage on deleted, renamedTo on transferred).

Crucially, valid calls still resolve to one of the five precise overloads first, so return-type precision – and therefore the existing inference behaviour that InferDurableNamespaces depends on – is completely unchanged. The union overload is only ever reached to produce a better error message.

Known limitation: this only improves the error when the state discriminant is present, since union excess-checking can't flag a stray property when it can't tell which member you meant (e.g. omitting state entirely and adding transferFrom).


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because: this is a type-only change to diagnostic wording. The set of inputs that compile vs error is unchanged (invalid input was already rejected, just with a worse message) and is exercised by the existing tsc type-check. The @cloudflare/config package has no type-error-assertion harness, and the message wording isn't practically assertable in automated tests. Verified locally against tsc that valid calls keep their precise return types and invalid calls now point at the offending property.
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: no user-facing API or behaviour change; only a TypeScript diagnostic message is improved.

Open in Devin Review

The `exports.durableObject()` factory has five overloads, one per Durable
Object `state`, each with a precise return type that downstream inference
(e.g. `InferDurableNamespaces`) relies on. When a call matches no overload,
TypeScript reports the *last* overload's error, so an invalid property such
as `storage` on a `renamed` entry produced the misleading message
`'"renamed"' is not assignable to type '"expecting-transfer"'`.

Add a final fallback overload typed against the full discriminated union.
When the precise overloads all fail, TypeScript falls back to it and, because
the parameter is a discriminated union, performs a discriminant-aware
excess-property check that points at the offending property directly:
`'storage' does not exist in type 'DurableObjectRenamedExportOptions'`.

Valid calls still resolve to a precise overload first, so return-type
precision and existing inference behaviour are unchanged.
@github-project-automation github-project-automation Bot moved this to Untriaged in workers-sdk Jul 10, 2026
@workers-devprod workers-devprod requested review from a team and dario-piotrowicz and removed request for a team July 10, 2026 13:06
@workers-devprod

workers-devprod commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • ✅ @cloudflare/wrangler
Show detailed file reviewers

@ask-bonk

ask-bonk Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

The change is a minimal, type-only addition. My analysis confirms:

  • The fallback overload is placed last, so precise overloads resolve first for valid calls — return-type precision and InferDurableNamespaces inference are preserved.
  • The implementation signature (options: DurableObjectExportOptions) already existed and is unchanged; the added declaration overload matches it exactly.
  • The added error-improvement mechanism (discriminant-aware excess-property check) is a well-understood TypeScript behavior.
  • No logic, security, or backward-compatibility concerns. The runtime implementation body is untouched.
  • No changeset is needed as this is a diagnostic-message-only change (though that's a judgment call, not a code bug).

The change is correct and low-risk. No actionable issues.

LGTM

github run

@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.

✅ Devin Review: No Issues Found

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

Open in Devin Review

@MattieTK MattieTK added the ci:no-changeset-required Skip pull request checks for a valid changeset label Jul 10, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 10, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

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

create-cloudflare

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

@cloudflare/deploy-helpers

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

@cloudflare/kv-asset-handler

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

miniflare

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

@cloudflare/pages-shared

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

@cloudflare/unenv-preset

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

@cloudflare/vite-plugin

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

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@14648

@cloudflare/workers-auth

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

@cloudflare/workers-editor-shared

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

@cloudflare/workers-utils

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

wrangler

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

commit: f9c4c80

@workers-devprod workers-devprod 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.

Codeowners reviews satisfied

@github-project-automation github-project-automation Bot moved this from Untriaged to Approved in workers-sdk Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:no-changeset-required Skip pull request checks for a valid changeset

Projects

Status: Approved

Development

Successfully merging this pull request may close these issues.

3 participants