odysseus-dev/odysseus · warning · Error

Cannot create reply: sender address is missing from this ema

Error message

Cannot create reply: sender address is missing from this email.

What it means

A local validation throw, not a network error: when building a reply/reply-all draft, the computed To address (from the original message's sender) is empty or whitespace, so the draft would have no recipient. Forward mode is exempt because the user supplies recipients.

Source

Thrown at static/js/emailInbox.js:929

        toAddress = originalToWithoutMe.join(', ') || originalCcWithoutMe[0] || data.from_address;
        ccAddresses = originalCcWithoutMe.filter(addr => !originalToWithoutMe.some(t => extractEmail(t) === extractEmail(addr))).join(', ');
      } else {
        // Build reply-all: TO = original sender, CC = everyone else (To + Cc minus me)
        ccAddresses = buildReplyAllCc(data, myAddresses);
      }
    } else if (mode === 'forward') {
      toAddress = '';
      subjectPrefix = 'Fwd: ';
    }

    // Don't double-prefix `Re:` / `Fwd:` when the subject already starts with one.
    // Replies to replies were producing `Re: Re: Re: …` which can also break
    // some IMAP servers' header parsing on very long subject lines.
    let _baseSubject = (data.subject || '').trim();
    if (subjectPrefix === 'Re: ' && /^re\s*:/i.test(_baseSubject)) subjectPrefix = '';
    else if (subjectPrefix === 'Fwd: ' && /^fwd?\s*:/i.test(_baseSubject)) subjectPrefix = '';
    if (mode !== 'forward' && !String(toAddress || '').trim()) {
      throw new Error('Cannot create reply: sender address is missing from this email.');
    }
    let content = `To: ${toAddress}\nSubject: ${subjectPrefix}${_baseSubject}`;
    if (ccAddresses) content += `\nCc: ${ccAddresses}`;
    if (mode !== 'forward' && data.message_id) content += `\nIn-Reply-To: ${data.message_id}`;
    if (mode !== 'forward' && data.message_id) content += `\nReferences: ${data.references ? data.references + ' ' + data.message_id : data.message_id}`;
    content += `\nX-Source-UID: ${em.uid}`;
    content += `\nX-Source-Folder: ${folderAtStart}`;
    if (data.attachments && data.attachments.length > 0) {
      const attStr = data.attachments.map(a => `${a.index}:${a.filename}:${a.size}`).join('|');
      content += `\nX-Attachments: ${attStr}`;
      if (mode === 'forward') content += `\nX-Forward-Attachments: 1`;
    }
    content += '\n---\n';

    // Format the original date in a human-readable way for the quote header
    let niceDate = data.date || '';
    try {
      if (data.date) {

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Open the raw message source and check what the From header actually contains
  2. For such messages, use Forward instead and type the recipient manually
  3. If you own the parser, ensure sender extraction falls back to Reply-To then From
  4. Improve the UX by pre-filling the compose window with an empty To instead of throwing, letting the user fill it

Example fix

// before
if (mode !== 'forward' && !String(toAddress || '').trim()) {
  throw new Error('Cannot create reply: sender address is missing from this email.');
}

// after — open compose with empty To instead of failing
if (mode !== 'forward' && !String(toAddress || '').trim()) {
  toAddress = '';
  uiModule.showToast('No sender address found — enter a recipient');
}
Defensive patterns

Strategy: validation

Validate before calling

const senderAddr = String(toAddress || '').trim();
if (mode !== 'forward' && !senderAddr) { openComposeWith({ to: '', subject: `${subjectPrefix}${_baseSubject}`, warn: 'No sender address on original — add a recipient' }); return; }

Type guard

function hasRoutableSender(data) { const v = String(data?.from_address || data?.sender || '').trim(); return /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v); }

Try / catch

try { if (mode !== 'forward' && !hasRoutableSender(data)) throw new Error('Cannot create reply: sender address is missing from this email.'); ... } catch (e) { uiModule.showError(e.message); }

Prevention

When it happens

Trigger: Clicking Reply/Reply-All on a message whose From header lacks a routable address — e.g. 'Undisclosed recipients', a malformed From with only a display name, or parser output that produced no email address. Only mode !== 'forward' path throws.

Common situations: Automated/noreply mailers with non-standard headers; a provider-specific header layout the parser does not extract sender from; corrupted message metadata in the fetched JSON.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/c27efa9f69eda8a1. Report an issue: GitHub.