The error sounds obscure but the cause is mundane: you added one email provider too many. SPF (RFC 7208, §4.6.4) caps the number of DNS-querying mechanisms a receiver will resolve at ten. Cross that line and evaluation stops with a permerror — SPF doesn’t “mostly work,” it fails outright. Because a broken SPF result can’t align, this also drags down DMARC. This guide shows exactly what counts toward the ten, why nested includes are the usual culprit, and how to get back under the limit without creating a fragile record.
The 10-lookup rule, precisely
The limit counts mechanisms that require a DNS query, evaluated recursively across every include. These cost a lookup each: include, a, mx, ptr, exists, and redirect. These do not cost a lookup: ip4, ip6, and all — they’re static, so use them freely. Two subtleties bite people:
- Includes nest. When you
include:_spf.google.com, that record itself contains more includes, and every one of those counts too. A single top-level include can consume three or four of your ten before you’ve added anything else. - An
mxmechanism can cost more than one. RFC 7208 also caps the number of MX records resolved at 10 permxlookup, and each name still resolves — a domain with many MX hosts is expensive.
The limit is per evaluation on the receiver’s side, not a count of lines in your record. That’s why a short-looking record can still PermError — the cost is hidden inside the includes.
A record that’s over budget
This looks reasonable — six providers, plus a and mx — but it’s well past ten once the includes expand:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com
include:sendgrid.net include:_spf.salesforce.com
include:mail.zendesk.com include:servers.mcsv.net
a mx ~allCounting the nested total: Google’s include alone expands to several, Outlook’s adds more, and each of SendGrid, Salesforce, Zendesk and Mailchimp contributes one or more — plus the a and the mx. The receiver stops at the tenth lookup and returns PermError; every mechanism after that point is never even read.
What PermError does to your mail
Most receivers treat permerror as equivalent to a hard SPF failure. In the Authentication-Results header it appears like this:
Authentication-Results: mx.google.com;
spf=permerror (google.com: too many DNS lookups) smtp.mailfrom=news@acme.com;
dmarc=fail (p=REJECT dis=REJECT) header.from=acme.comSPF didn’t just fail to pass — it errored, so there is no aligned SPF identifier for DMARC to lean on. If DKIM is also unaligned, DMARC fails, and under p=reject the message is refused. Worse, this is silent: nothing in your sending platform warns you. You find out from bounces or from reading a header, which is why periodic checking matters. To decode that header field by field, see how to read the Authentication-Results header.
Step 1 — count and see the tree
Before changing anything, measure. The SPF Include Tree expands every nested include and shows you the running lookup count, so you can see exactly which provider is the expensive one — Google and Microsoft are frequently the biggest single contributors. Pair it with the SPF Checker to confirm the current pass/fail and total. You can’t consolidate what you haven’t measured.
Step 2 — consolidate the safe way
Work through these in order; each is low-risk and often enough on its own:
- Remove senders you no longer use. Old ESPs, a CRM you migrated off, a form tool from a campaign two years ago — every stale include is a free lookup back. This is the single highest-value cleanup.
- Drop redundant
aandmx. These are often left over from a default template. You only needmxif your MX hosts actually send mail; many domains receive on their MX but send through an ESP, making themxmechanism pure waste. - Replace an
a/mxwith an explicitip4/ip6when the address is stable. Static IP mechanisms cost zero lookups. - Move a whole sending stream to a subdomain. Send marketing from
news.acme.comwith its own SPF record and your root domain’s record shrinks. This also cleanly separates transactional and marketing mail.
Very often, removing two dead includes and a needless mx drops you comfortably under ten and you’re done — no flattening required.
Step 3 — flatten, carefully, only if you must
“Flattening” means replacing an include: with the actual ip4/ip6 ranges it currently resolves to, so those addresses cost no lookups. It reliably fixes the count — but it trades a maintenance burden you didn’t have before, and the risks are real:
- Provider IPs change without notice. When Google or SendGrid adds or retires a sending range, their
includeupdates automatically — your flattened copy does not. Stale ranges mean legitimate mail suddenly SPF-fails. - Records have length limits. A single TXT string maxes at 255 characters and the whole record at 512 bytes over UDP; dumping dozens of ranges inline can overflow and create new problems.
- You inherit an operational job. A flattened record must be re-checked on a schedule, effectively forever. If you flatten, prefer flattening only stable senders (your own static IPs, a fixed relay) and leave volatile provider includes as real includes.
A pragmatic hybrid record keeps the big, well-maintained provider include intact while flattening your own known-stable IPs:
v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24
ip4:203.0.113.16/28 include:_spf.google.com ~allAfter any change, re-run the SPF Include Tree to confirm the total is at or below ten, and the SPF Checker to confirm it still passes for your real sending IPs.
Also watch: two other SPF traps
- Only one SPF record allowed. Two
v=spf1TXT records on the same domain is itself a PermError. Merge every sender into one record — never publish a second. - The
void lookuplimit. RFC 7208 also caps lookups that return no answer (NXDOMAIN / empty) at two; includes pointing at dead hostnames burn that budget and error out. Remove includes that no longer resolve.
Verify and move on
Once you’re back under ten and SPF passes, confirm the whole authentication picture: run the DMARC Checker to make sure alignment now holds, and the DKIM Checker so DKIM can back up SPF on forwarded mail. If SPF passes but DMARC still fails, the issue is alignment, not lookups — see why SPF passes but DMARC fails. For the complete model of all three records, read the complete SPF, DKIM & DMARC guide, and once everything is clean, tighten policy with how to move DMARC to p=reject safely. The authoritative source for every rule above is RFC 7208; Google’s SPF limits guidance and Microsoft’s SPF configuration docs cover the provider-specific includes.