What versions & operating system are you using?
main (fb6b51b), Node v22.17.1, macOS 15. Reproduced against packages/miniflare in the monorepo; the core of it reproduces in plain Node with no Workers involved.
Please provide a link to a minimal reproduction
Inlined below — it's six lines of plain JS against the replacer as it's currently written, so a linked repo would only add noise.
const SERIALIZED_DATE = "___serialized_date___";
const replacer = (_, value) =>
value instanceof Date ? { [SERIALIZED_DATE]: value.toISOString() } : value;
console.log(JSON.stringify({ scheduledTime: new Date(0) }, replacer));
// {"scheduledTime":"1970-01-01T00:00:00.000Z"} <- no tag, so nothing to revive
Describe the Bug
When tail events are forwarded between two local dev sessions through the dev registry, Date values arrive at the tail consumer as ISO strings rather than Date objects. event.scheduledTime.getTime() throws in local dev, while the same tail handler works in production. TraceItemAlarmEventInfo.scheduledTime is typed Date, so the types promise something local dev doesn't deliver.
The forwarding path serialises with JSON.stringify(events, tailEventsReplacer) and revives with tailEventsReviver (packages/miniflare/src/workers/core/dev-registry-proxy-shared.worker.ts). The replacer is meant to tag Dates so the reviver can rebuild them:
export function tailEventsReplacer(_: string, value: any) {
if (value instanceof Date) {
return { [SERIALIZED_DATE]: value.toISOString() };
} else if (typeof value === "bigint") {
return { [SERIALIZED_BIGINT]: value.toString() };
}
return value;
}
JSON.stringify() applies Date.prototype.toJSON() before it calls the replacer, so a real Date reaches the replacer already flattened to an ISO string. value instanceof Date is therefore never true for it, no tag is written, and the reviver has nothing to restore. The bigint arm is unaffected, which is why that half of the round-trip works.
Worth noting the instanceof check isn't dead: a value whose own toJSON() returns a Date reaches the replacer unconverted and is tagged correctly today, so that behaviour should be preserved rather than swapped out.
packages/vite-plugin-cloudflare/src/workers/vite-proxy-worker/index.ts carries its own copy of the pair with the same Date behaviour. The two aren't identical — the vite copy has no bigint handling — so they're the same Date issue in two places rather than one shared implementation.
This is a local-dev fidelity problem (dev diverging from production), not a production issue.
Please provide any relevant error logs
AssertionError: expected '2025-05-01T12:34:56.000Z' to be an instance of Date
I have a fix with tests and will open a PR shortly.
What versions & operating system are you using?
main(fb6b51b), Node v22.17.1, macOS 15. Reproduced againstpackages/miniflarein the monorepo; the core of it reproduces in plain Node with no Workers involved.Please provide a link to a minimal reproduction
Inlined below — it's six lines of plain JS against the replacer as it's currently written, so a linked repo would only add noise.
Describe the Bug
When tail events are forwarded between two local dev sessions through the dev registry,
Datevalues arrive at the tail consumer as ISO strings rather thanDateobjects.event.scheduledTime.getTime()throws in local dev, while the same tail handler works in production.TraceItemAlarmEventInfo.scheduledTimeis typedDate, so the types promise something local dev doesn't deliver.The forwarding path serialises with
JSON.stringify(events, tailEventsReplacer)and revives withtailEventsReviver(packages/miniflare/src/workers/core/dev-registry-proxy-shared.worker.ts). The replacer is meant to tagDates so the reviver can rebuild them:JSON.stringify()appliesDate.prototype.toJSON()before it calls the replacer, so a realDatereaches the replacer already flattened to an ISO string.value instanceof Dateis therefore never true for it, no tag is written, and the reviver has nothing to restore. Thebigintarm is unaffected, which is why that half of the round-trip works.Worth noting the
instanceofcheck isn't dead: a value whose owntoJSON()returns aDatereaches the replacer unconverted and is tagged correctly today, so that behaviour should be preserved rather than swapped out.packages/vite-plugin-cloudflare/src/workers/vite-proxy-worker/index.tscarries its own copy of the pair with the sameDatebehaviour. The two aren't identical — the vite copy has nobiginthandling — so they're the sameDateissue in two places rather than one shared implementation.This is a local-dev fidelity problem (dev diverging from production), not a production issue.
Please provide any relevant error logs
I have a fix with tests and will open a PR shortly.