Skip to content

Worker: domain prop is read without resolving per-field Effect inputs (crashes domain reconciliation) #588

Description

@agcty

Repro (2.0.0-beta.55)

The class-form Worker/StaticSite pattern relies on a plain props literal so InferEnv<typeof Website> can infer the binding map. Stage-dependent values are therefore expressed as per-field Effect inputs (the InputProps machinery), not by branching the whole props object:

export class Website extends Cloudflare.StaticSite<Website>()("website", {
  // ...
  url: Alchemy.Stack.useSync(stack => stack.stage !== "prod"),
  domain: Alchemy.Stack.useSync(stack => (stack.stage === "prod" ? "example.com" : undefined)),
  env: { /* bindings — per-field Effects here resolve fine */ },
})

Deploying any stage fails at the final step:

[Worker] Reconciling custom domains (1) ...
[Worker] fail
ERROR: SchemaError: Expected string | undefined, got {"~effect/Effect/args":(fiber) => ...}
  at ["hostname"]
    at GET /accounts/{account_id}/workers/domains

Cause

Worker.ts feeds the raw prop into normalizeDomains(news.domain) without resolving Input effects in that path. Env bindings and other props resolve fine — this domain path reads news directly. Two consequences:

  1. The unresolved Effect object is neither undefined nor an array, so it normalizes to a one-element domain list — reconciliation runs even on stages where the value would resolve to undefined.
  2. The object reaches the API client as hostname and fails schema validation.

news.url !== false a few lines down looks like the same unresolved read — an Effect object is truthy, so a url that resolves to false on some stages would still enable the workers.dev subdomain there.

Why per-field inputs (not in-generator construction)

The obvious alternative — construct the Worker inside the stack generator with different props per stage — breaks InferEnv: once the props object is produced by a generator rather than a literal, the binding-map type is lost, so Cloudflare.InferEnv<typeof Website> (used to type env.d.ts) no longer works. Per-field Effect inputs are the only shape that keeps the class-form inference and lets values vary by stage. That makes consistent input resolution across props a requirement of the class form you ship, not a request for a new pattern.

Expected

domain and url resolve per-field Effect inputs on the provider path the same way env bindings already do.

Happy to PR the fix if you point me at the intended resolution point for that path (same fork as #586/#587).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions