Skip to content

Auto-draft trigger fails with 500 for mailboxes never opened in the UI #57

Description

@anarkrypto

Summary

receiveEmail() triggers the auto-draft agent by calling the EmailAgent Durable Object stub directly. That call fails with HTTP 500 for any mailbox whose agent instance has never been named — in practice, any mailbox that was never opened in the UI. The failure is swallowed by the surrounding .catch(), so the email is stored normally and nothing surfaces the problem.

Net effect: the "Auto-draft on new email" feature advertised in the README silently never runs for those mailboxes.

Where

https://github.com/cloudflare/agentic-inbox/blob/main/workers/index.ts — end of receiveEmail():

const agentStub = env.EMAIL_AGENT.get(env.EMAIL_AGENT.idFromName(mailboxId));
ctx.waitUntil(agentStub.fetch(new Request("https://agents/onNewEmail", {
    method: "POST", headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ mailboxId, emailId: messageId, ... }),
})).catch((e) => console.error("Auto-draft trigger failed:", (e as Error).message)));

Root cause

partyserver's Server.fetch() requires the instance to have a name. It first tries hydrateNameFromStorage(), and if that yields nothing it demands an x-partykit-room header (partyserver@0.3.3, dist/index.js):

if (!this.#_name) await this.#hydrateNameFromStorage();
if (!this.#_name) {
    const room = request.headers.get("x-partykit-room");
    if (!room) throw new Error(`Missing namespace or room headers when connecting to ${this.#ParentClass.name}. ...`);
    await this.setName(room);
}

An instance only gets a persisted name when something performs the set-name handshake. The UI does it implicitly: browser connections go through the /agents/* route, which resolves namespace and room from the URL. The email handler bypasses that and calls the DO directly with only Content-Type, so a fresh instance has no name and throws.

This makes the bug look intermittent: a mailbox that has been opened in the UI at least once works, because its name is already in storage. A mailbox that has only ever received email never does.

Reproduction

  1. Create a mailbox and do not open it in the UI.
  2. Send an email to it.
  3. wrangler tail.

Observed:

{
  "entrypoint": "EmailAgent",
  "event": { "request": { "url": "https://agents/onNewEmail", "method": "POST" },
             "response": { "status": 500 } },
  "logs": [{ "level": "error", "message": [
      "Error in EmailAgent:<unnamed> fetch:",
      "Error: Missing namespace or room headers when connecting to EmailAgent.\nDid you try connecting directly to this Durable Object? Try using getServerByName(namespace, id) instead."
  ]}]
}

The email itself is stored fine — findThreadBySubject and createEmail both return ok before the agent call fails.

Fix

Resolve the agent through the SDK, which performs the set-name handshake before returning the stub:

import { getAgentByName } from "agents";

ctx.waitUntil(
    getAgentByName(env.EMAIL_AGENT, mailboxId)
        .then((agentStub) => agentStub.fetch(new Request("https://agents/onNewEmail", {
            method: "POST", headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ mailboxId, emailId: messageId, ... }),
        })))
        .catch((e) => console.error("Auto-draft trigger failed:", (e as Error).message)),
);

Verified in production. After the change, on a mailbox created via R2 and never opened in the UI:

ok  EmailAgent  http://dummy-example.cloudflare.com/cdn-cgi/partyserver/set-name/
ok  MailboxDO   getEmail
ok  MailboxDO   getEmails
ok  MailboxDO   getThreadEmails

The set-name handshake is the step that was missing; the agent then reads the mailbox and drafts as intended.

Secondary observation

Because the call sits inside ctx.waitUntil(...).catch(console.error), this failed on every inbound email without any user-visible signal. Surfacing agent-trigger failures somewhere more visible than console.error would make this class of bug easier to catch. Related: #17 ("Log silent catches in deep-scan pipeline") touches the same theme.

Environment

  • agents@0.7.6, partyserver@0.3.3
  • wrangler@4.74.0, compatibility_date 2025-11-28
  • Deployed on Workers with Email Routing catch-all → Worker

Happy to open a PR with the fix if useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions