IDLE
A folder must be selected (see "SELECT folder") before running this function.
IDLE (RFC 9051 §6.3.13) lets the server push unsolicited updates (new mail, expunges, flag changes, ...) as they happen, instead of you polling. It is the IMAP way of "watching a mailbox".
Usage
await imap.idle((item) => {
console.log(item.line) // "* 5 EXISTS", "* 2 EXPUNGE", "* 1 FETCH ...", ...
// return false to leave IDLE
})
The callback receives every untagged response ({ line, literal }) while idling. Return false from it to stop idling — the library then sends DONE and waits for the tagged OK.
IDLE ends cleanly (the connection stays usable) in three ways:
- the callback returns
false, - no updates arrive within the read timeout (
timeoutMs, default 30000) — this is a clean exit, not an error; re-issueidle()to keep watching, - the server ends IDLE itself (the library detects the tagged completion and does not send
DONE).
Example — wait for new mail
// Wait until a new message arrives in INBOX
await imap.idle((item) => {
if (item.line.startsWith("* ") && item.line.endsWith(" EXISTS")) {
console.log("New mail!")
return false // stop idling
}
})
Example — keep watching (re-idle loop)
Servers SHOULD end IDLE after ~30 minutes (RFC 9051), and most clients re-enter IDLE periodically anyway. Because a quiet mailbox times out cleanly, a watch loop is just a while:
for (;;) {
await imap.idle((item) => {
console.log(item.line)
// return false to leave IDLE early
})
// The timeout ended IDLE (or the server did) — the connection is still
// usable, re-enter IDLE to keep watching.
}
IDLE on Cloudflare Workers
IMAP connections created with cloudflare:sockets live and die with the Worker invocation — there is no way to hold a socket open between requests. IDLE is therefore "wait for updates during one invocation". Within that model it works well:
- HTTP-triggered requests have no wall-clock limit while the client stays connected, and waiting on the socket costs ~0 CPU time (even the free plan's 10 ms CPU cap is not consumed). A worker can stream updates to a browser via SSE/WebSocket for as long as the client is connected.
- Cron Triggers / Queue consumers / Durable Object alarms are capped at 15 minutes of wall time per invocation — re-enter
idle()(or reconnect) inside that window. This lines up with the RFC's recommendation to re-idle periodically. - Durable Objects: an open outbound TCP socket keeps the DO alive for up to 15 minutes per connection (after that, standard eviction rules resume) — reconnect or re-idle before then.
- Billing: on the paid plan, duration (wall-clock time) is billed per second; long IDLE sessions accumulate duration. On the free plan there are no duration charges.
Limitations
- While IDLE is active no other command may be issued on the connection (the library rejects them with
Another IMAP command is already in progress...). - If the server closes the connection mid-IDLE (crash, restart),
idle()throws — re-runconnect()before re-idling. - IDLE is part of the base IMAP4rev2 protocol; IMAP4rev1 servers need the
IDLEcapability.