Repository navigation
Description
Motivation
manuallyStartQueue defaults to false, so Fedify calls _startQueueInternal() automatically from inbox handling and from sendActivity(). Those call sites do not await or catch the returned promise.
WorkersMessageQueue.listen() throws a TypeError because Cloudflare Queues cannot be polled, as documented at packages/cfworkers/src/mod.ts:470-479. _startQueueInternal() is async, so the synchronous throw becomes a rejected promise, and the call site discards it. The first request that reaches one of these paths produces an unhandled rejection from the first queue whose listen() throws; later requests skip that queue because it has already been marked as started.
With WorkersMessageQueue, the queue itself is consumed by the Worker's queue() handler, so no message is lost. The rejection is still a real bug: it adds noise to production logs, trains users to ignore unhandled rejections, and can mask an actual startup failure from another queue.
This is the same Workers lifetime problem as #661, but on the automatic queue startup path rather than the fanout enqueue path.
The automatic call sites on main are middleware.ts:3785, :4924, :6609 and :7657, and tasks/enqueue.ts:96. On 2.0-maintenance they are middleware.ts:1480, :1778 and :2691.
Expected behavior
Automatic startup failures should be logged once and not become unhandled rejections. Explicit Federation.startQueue() should keep rejecting so a caller that starts workers deliberately sees the error.
Fix
Wrap each automatic call:
void this._startQueueInternal(data).catch((error) => {
getLogger(["fedify", "federation", "queue"]).warn(
"Failed to start queue workers automatically: {error}",
{ error },
);
});Inside _startQueueInternal(), attach a handler to each listen() promise before awaiting the aggregate, so one synchronous throw does not strand listeners that were already started.
Also update the Cloudflare Workers example, docs/manual/deploy.md, and docs/manual/mq.md to set manuallyStartQueue: true. On Workers this is the supported configuration, and the warning then does not appear at all.
Scope
- Fix
2.0-maintenancefirst at middleware.ts:1480, :1778 and :2691, then forward-port to later maintenance branches andmain. - No public API change.
- Add a regression test with a fake queue whose
listen()throws. The test should assert thatfetch()does not emit an unhandled rejection and that explicitstartQueue()still rejects.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Activity
waitUntilhook for serverless request lifetimes #1383WorkersMessageQueuesends alive with awaitUntiloption #1386