We have a Comms pipeline that reads contact@expedisai.com from Hostinger, routes each email through an AI agent that drafts a reply, and queues the draft for human approval. Every inbound email gets a draft in under 90 seconds; the team reviews and approves with one click. The problem was that the pipeline kept crashing before it read a single email.
What Was Breaking
n8n's emailReadImap node has a structural quirk: it registers as a persistent IMAP IDLE trigger regardless of where it sits in a workflow. IDLE is a protocol mode where the client holds an open connection to the mail server and waits for push notifications about new mail. Hostinger's IMAP server supports it, but the n8n handshake kept failing. When it failed, n8n deactivated the workflow, then reactivated it, then failed again, in a loop. The logs filled with "No mailbox is currently selected" errors. The workflow was cycling dozens of times per hour without processing anything.
The fix people usually reach for is IMAP credential debugging or server-side timeout settings. We tried both. The issue was not credentials — the connection itself succeeded. The issue is structural: the emailReadImap node is designed to be a trigger, and using it mid-workflow forces it into persistent connection mode that the orchestrator cannot recover cleanly when the connection drops. The crash loop is the symptom of that mismatch, not a configuration problem.
Moving IMAP Out of n8n Entirely
We removed the emailReadImap node and replaced it with a standard HTTP Request node pointing at a new API route on the dashboard: /api/email-agent/fetch. The route connects to Hostinger IMAP directly using the imapflow Node.js library, reads the inbox, and returns JSON. n8n calls it like any other HTTP endpoint. The IMAP connection is stateless — it opens, reads, closes. No persistent IDLE, no crash loop.
The route accepts a ?days=N query parameter (default 7, max 30). It does a two-pass fetch: first pull all envelope metadata (uid, from, subject, date) for messages since the cutoff date, then fetch the plain text body for each uid in a second pass. The two-pass approach is necessary because imapflow requires separate calls for envelope data versus body parts.
The route runs with export const runtime = 'nodejs' because imapflow is CommonJS and requires Node APIs not available in the Edge runtime. We added the path to the middleware public routes list so n8n can reach it from inside its Docker container without a session cookie. n8n uses http://host.docker.internal:3001 to call through to the host machine's dev server.
On n8n's side, two new nodes replaced the single broken one: Fetch Emails (HTTP Request) and Expand Emails (Code node that maps the returned array into individual items for the SplitInBatches loop). The rest of the workflow — deduplication, Hub AI call, database write — was unchanged.
The Hidden Data Flow Bug
Once the IMAP crash was fixed, executions ran but emails failed to save. The error was a SQL string with a literal undefined where a message ID should be.
The cause was how n8n's Postgres executeQuery node works. The deduplication step runs SELECT COUNT(*) as cnt and returns { cnt: 0 }. That result replaces the item data in $json — the original email fields (message_id, from_email, subject, body) are gone by the time the next node runs. The IF node downstream passes through { cnt: 0 }. The Save node then tries to read $json.message_id, gets undefined, and the SQL string breaks.
The fix: instead of const d = $json, we changed both the Save node and the Build Hub Message node to use const d = $('Parse Email').item.json. In a SplitInBatches loop, this expression gives you the current iteration's output from a named node, regardless of what intermediate nodes did to $json. Any time you have a Postgres query node mid-loop, the nodes after it need to reference earlier nodes explicitly rather than assuming $json still holds the original item.
Result
After both fixes, the first clean execution processed 7 days of Hostinger INBOX — 72 emails total. The Hub AI agent drafted contextual replies for each one. Final state: 70 emails in queued_approval, ready for one-click approve or reject on the Comms page. The 30-minute cron continues scanning automatically, and new emails arrive with drafts attached before the team opens their browser in the morning.
The takeaway on the IMAP architecture: n8n's native email node is a trigger, and it behaves like one even when you put it in the middle of a workflow. If you need reliable scheduled IMAP reads as part of a multi-step automation, put the IMAP logic in a service you control and call it over HTTP. Stateless call-and-return is more reliable than a persistent connection managed by the orchestrator.
.png&w=384&q=75)