#39683·n8n

Email Trigger (IMAP) crashes n8n process when a fetched message has no attributes

Author: MastermindRS6Created Sep 27, 2026Updated Sep 28, 2026
LabelsNeeds Feedbackteam:nodesstatus:in-linearstatus:team-assigned

Bug Description

getNewEmails() in the Email Trigger (IMAP) node (nodes/EmailReadImap/v2/utils.ts) assumes every message returned by imapConnection.search(...) has a populated attributes object. When a message in the result set has attributes === undefined, the first read of message.attributes.uid throws a TypeError inside the trigger's poll cycle. This is an uncaught exception, so it doesn't just fail one execution - it kills the trigger ("failed at runtime and was deactivated"), and in our case brought down the whole n8n process.

We also found that a naive fix (just skipping messages without attributes) exposes a second bug: the do/while retry loop in the same function only checks results.length >= EMAIL_BATCH_SIZE to decide whether to loop again - it never checks whether the loop made forward progress (maxUid advancing). If an entire batch lacks usable attributes, maxUid stays at 0 forever and the loop re-fetches the same batch indefinitely, ballooning memory until the OS OOM-kills the process.

Full write-up with stack trace, code, and a suggested fix is in the "Debug Info" section below.

To Reproduce

  1. Set up the Email Trigger (IMAP) node against an IONOS mailbox (imap.ionos.de) with trackLastMessageId enabled on a live production mailbox with hundreds/thousands of messages.
  2. Restart the n8n instance (Docker container) so the trigger re-establishes its IMAP connection and polls for new messages.
  3. On our instance this reliably produced at least one message in the search result whose attributes object was undefined - including polls that found zero genuinely new messages.
  4. The trigger throws "Cannot read properties of undefined (reading 'uid')", logs "The trigger node ... failed at runtime and was deactivated", and in our environment crashed the whole n8n Node process (Docker's restart policy then brought up a fresh container, which hit the same crash again on its own first poll).

Expected behavior

The trigger should skip a malformed/attribute-less message gracefully (log a warning and continue) instead of throwing an uncaught exception that kills the trigger and can crash the whole n8n process.

Debug Info

Note: this is not the literal "Copy debug information" output (our instance was mid-incident and heavily patched at the time), but here is the full technical detail:

Stack trace:

TypeError: Cannot read properties of undefined (reading 'uid')
    at TriggerContext.getNewEmails (.../EmailReadImap/v2/utils.ts:111:59)
    at processTicksAndRejections (node:internal/process/task_queues:104:5)
    at fetchNewEmails (.../EmailReadImap/v2/EmailReadImapV2.node.ts:307:5)

Followed by:

Email Read Imap node encountered an error fetching new emails
The trigger node "E-Mails lesen (IONOS)" failed at runtime and was deactivated

Root cause, compiled dist/nodes/EmailReadImap/v2/utils.js (~lines 64-86):

javascript
async function getNewEmails({ onEmailBatch, imapConnection, postProcessAction, searchCriteria, }) {
    let criteria = searchCriteria;
    let results = [];
    let maxUid = 0;
    do {
        if (maxUid) {
            criteria = criteria.filter(...);
            criteria.push(['UID', `${maxUid}:*`]);
        }
        results = await imapConnection.search(criteria, FETCH_OPTIONS[format] ?? {}, limit);
        const newEmails = [];
        const processedUids = [];
        for (const message of results) {
            const lastMessageUid = staticData.lastMessageUid;
            if (lastMessageUid !== undefined && message.attributes.uid <= lastMessageUid) // throws if message.attributes is undefined
                continue;
            if (message.attributes.uid > maxUid)
                maxUid = message.attributes.uid;
            ...
        }
        ...
        if (maxUid > (staticData.lastMessageUid ?? 0)) {
            staticData.lastMessageUid = maxUid;
        }
    } while (results.length >= EMAIL_BATCH_SIZE); // no progress check
}

Suggested fix:

javascript
for (const message of results) {
    if (!message.attributes) { continue; } // skip malformed entries instead of crashing
    const lastMessageUid = staticData.lastMessageUid;
    if (lastMessageUid !== undefined && message.attributes.uid <= lastMessageUid)
        continue;
    ...
}
javascript
let maxUid = 0;
do {
    const previousMaxUid = maxUid; // track progress
    ...
} while (results.length >= EMAIL_BATCH_SIZE && maxUid > previousMaxUid); // require forward progress

We patched exactly this directly in a running container for our own recovery, and both the instant-crash-on-poll and the runaway-memory loop stopped. We can't rule out a third, slower memory-growth issue under normal operation that we didn't have time to isolate separately, but these two are clearly real and independent of that.

Additional environment notes: 1 vCPU / 4GB RAM VPS, Docker image n8nio/n8n:latest pulled ~2026-09-26. Tested NODE_OPTIONS=--max-old-space-size at 1536/2048/3072 - the crash-on-poll happened identically regardless, confirming it is not a heap-size issue.

Operating System

Ubuntu 25.10

n8n Version

2.40.7

Node.js Version

26.7.0

Database

SQLite (default)

Execution mode

main (default)

Hosting

self hosted