Sieve rules don't run on imported or fetched mail

2026-08-25 · Nikhil “Nick” Harinath · ~3 min

You write a Sieve rule. The syntax validates. The logic is obviously right. You send yourself a test message and it files perfectly.

Then real mail arrives and the rule does nothing at all. Every time.

The answer

Sieve executes at delivery. It is part of the mail delivery agent's pipeline: a message arrives over SMTP, the server runs your script, the script decides where it lands.

If your mail isn't delivered to that account — if it's pulled in from another provider by POP, IMAP migration, or a "fetch from another account" feature — it never enters the delivery pipeline. The server writes it straight into a folder. Sieve is not consulted, and no error is produced, because from the server's point of view nothing went wrong.

Your rule isn't broken. It's just never being asked.

How to tell in one test

Send a message directly to the address on the Sieve-hosting server — the native address, not the account you're forwarding or fetching from.

  • Files correctly → Sieve works; your real mail is arriving by a path that bypasses it.
  • Also does nothing → now you have a genuine Sieve problem worth debugging.

That single test separates "my script is wrong" from "my script is irrelevant", and they need completely different fixes.

What to do instead

If you control the routing, change how mail arrives. Forwarding at the source means real SMTP delivery, which means Sieve runs. This is the clean fix when it's available.

If you can't — and you often can't, because the account you're consolidating won't forward, or you need the mail to stay in both places — then server-side filtering is not available to you. Accept it and move the logic client-side: a scheduled job over IMAP or JMAP that does the classification and filing you wanted Sieve to do.

That's a real architectural consequence, not a workaround. It means filing latency is now your job scheduler's interval rather than instant, and it means the logic lives somewhere you have to run and monitor.

Two adjacent traps in the same territory

Migration folders look like your inbox but aren't. An import typically creates something like Imported 20260101/INBOX containing the entire history. That's an archive. New mail still lands in the real inbox, and they are different folders with different IDs. Anything you script should target the folder's role (inbox, archive, sent) rather than its display name — names vary by provider and by import, roles don't.

Header syntax is provider-specific. In JMAP, a header property is written as header:List-Unsubscribe. Adding a type suffix that some documentation shows — header:List-Unsubscribe:asText — gets rejected outright with invalidArguments on at least one major provider. If a header query fails validation, try the bare form before assuming your filter logic is wrong.

The general lesson

Before debugging a rule that doesn't fire, verify the rule is even on the path. A filter that never executes and a filter that executes incorrectly produce the same observable result — nothing happens — and they share no fixes at all.

Related notes

← All notes  ·  Work with me