Miniflare email() handler message has no canBeForwarded property in local dev

Open Beginner friendly
#16,790 3 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@mindulle is already working on this.

Since Nov 10, 2026.

  • #16811 by @mindulle — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Domain
backend, cli

Research direction

Start in packages/miniflare/src/workers/core/email.ts at the object passed to the Worker’s email() handler, and compare its fields with the ForwardableEmailMessage behavior described in the issue. Run the inline wrangler dev reproduction and confirm canBeForwarded exists as a boolean while rawSize remains 147. The issue leaves the boolean’s value for locally posted mail to maintainers.

Written by the indexing model from the issue text.

Description

bug package:miniflare product:email
What versions & operating system are you using?
  • wrangler 4.158.0 (latest on npm at the time of testing), which depends on miniflare 5.20261104.0-beta and workerd 1.20261104.1
  • Node.js 26.12.1, npm 12.2.1, macOS 27.2 (arm64)
  • Source inspected at workers-sdk main commit 33600fc3930902a0e5c9146d0d0c82f0029ddfa9
  • compatibility_date 2026-11-01
Please provide a link to a minimal reproduction

The reproduction is inline under Describe the Bug (two files, wrangler.jsonc and src/index.js, plus a msg.eml fixture); there is no separate repository.

Describe the Bug

In wrangler dev, the ForwardableEmailMessage passed to a Worker's email() handler has no canBeForwarded property: message.canBeForwarded is undefined and "canBeForwarded" in message is false. Production exposes canBeForwarded as a read-only boolean on every inbound message, and the Email Service docs list it, so handler code that tests it for truthiness before calling message.forward() takes a different branch locally than in production.

Steps to reproduce
  1. Create a project with wrangler.jsonc:
{
  "name": "email-can-be-forwarded-repro",
  "main": "src/index.js",
  "compatibility_date": "2026-11-01"
}
  1. Create src/index.js:
export default {
  async email(message) {
    console.log("canBeForwarded:", message.canBeForwarded, "| in message:", "canBeForwarded" in message, "| rawSize:", message.rawSize);
  },
};
  1. Create msg.eml with CRLF line endings:
From: [email protected]
To: [email protected]
Subject: hello
Message-ID: <[email protected]>
Date: Sun, 08 Nov 2026 12:00:00 +0000

body
  1. Run npx [email protected] dev --port 8812 with the versions listed above.
  2. Run curl -X POST 'http://localhost:8812/cdn-cgi/handler/[email protected]&[email protected]' --data-binary @msg.eml.
Expected behavior

The handler should log a boolean for canBeForwarded and true for "canBeForwarded" in message. The production ForwardableEmailMessage declares JSG_READONLY_PROTOTYPE_PROPERTY(canBeForwarded, getCanBeForwarded) returning a bool, and the Email Service email handler reference lists readonly canBeForwarded: boolean; // Whether the message can be forwarded on the message parameter. Which value Miniflare should report for a locally posted message is for the maintainers to decide; the property should exist and be a boolean.

Actual behavior

The curl request returned Worker successfully processed email with HTTP 200, and wrangler dev logged:

canBeForwarded: undefined | in message: false | rawSize: 147

rawSize and the other documented properties are present. A second run with a different handler on the same versions logged canBeForwarded = undefined | 'canBeForwarded' in message = false for each of four inbound messages.

Evidence
  • Expected source: src/workerd/api/email.h lines 256-258 (getCanBeForwarded) and 278 (JSG_READONLY_PROTOTYPE_PROPERTY(canBeForwarded, getCanBeForwarded);) in cloudflare/workerd#8222 at head b3267af57e368a5c22e5a5af86fd42bb66d5fdd4, an open PR whose description says the internal production code is moved "as-is"; and src/content/docs/email-service/api/route-emails/email-handler.mdx line 64 in cloudflare/cloudflare-docs (published at https://developers.cloudflare.com/email-service/api/route-emails/email-handler/).
  • Failure source: the local wrangler dev run above; source breadcrumb: the object literal passed to the user Worker's email() in packages/miniflare/src/workers/core/email.ts (lines 257-382, ending in satisfies ForwardableEmailMessage) sets from, to, raw, rawSize, headers, setReject, forward and reply, and no canBeForwarded.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The local behavior was observed in wrangler dev. The production property is static evidence from the workerd PR source and the docs; it was not observed live, because the investigating account has no zone with Email Routing.

Restoration check

With the reproduction above, the handler currently logs canBeForwarded: undefined | in message: false. After the fix, the same request logs canBeForwarded: true or canBeForwarded: false (a boolean) and in message: true, and still logs rawSize: 147 for the same message.

Additional context

The ForwardableEmailMessage interface in the published @cloudflare/workers-types 5.20261108.1 does not declare canBeForwarded either, so the satisfies ForwardableEmailMessage check in Miniflare cannot flag the missing property. That types gap is tracked in cloudflare/workerd#8290; this report covers only Miniflare's runtime object, which will also stop satisfying the interface once the types add the property.

References
  • cloudflare/workers-sdk#16788: a separate Miniflare email local-dev discrepancy (name-less EmailAddress in send_email).
  • cloudflare/workerd#8290: canBeForwarded missing from ForwardableEmailMessage in @cloudflare/workers-types.
Please provide any relevant error logs

wrangler dev output after the curl request:

[wrangler:info] Ready on http://localhost:8812
canBeForwarded: undefined | in message: false | rawSize: 147
Dominant language
TypeScript
Stars
4.7k
Forks
1.6k
Avg merge
1d 23h
Merged PRs (30d)
268

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from cloudflare/workers-sdk

All issues in cloudflare/workers-sdk

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.