What PermError means to a receiver
RFC 7208 gives an SPF check seven possible results. Five of them are about the message: pass, fail, softfail, neutral and none. Two are about the record itself: temperror (the receiver could not complete the DNS lookups right now, try later) and permerror (the record is broken and no amount of retrying will help). A receiver that hits PermError is told to treat the domain as if it had published nothing. Gmail shows it in the Authentication-Results header as spf=permerror, and if DKIM is not aligned as well, DMARC fails on that message.
The unsettling part is that nothing tells you. Mail keeps being sent, most of it keeps being delivered because DKIM carries it, and the only signal is a slow rise in spam placement, or a line in a DMARC aggregate report you may not be reading. I have seen SPF broken this way for over a year on a domain that everyone assumed was fine.
The four causes, most common first
1. More than ten DNS lookups
Every include:, a, mx, ptr, exists: and redirect= term costs one DNS query, and the includes are followed recursively, so their own include and mx terms count too. The limit is ten across the whole tree. Adding one SaaS provider whose include pulls in three more is enough to cross it. The SPF checker draws the tree and shows the running count, so you can see exactly which include tipped it over. The fixes are in the SPF 10 DNS lookup limit: remove providers that no longer send, replace a and mx with ip4:/ip6: where the addresses are stable, or move rarely used senders onto a subdomain with its own record.
2. Two SPF records
A domain may publish exactly one TXT record starting v=spf1. Two is a PermError, even if they are identical, even if one of them is correct. This happens when a second team or a second provider's setup wizard adds a record rather than editing the existing one. Check with a TXT lookup on the bare domain and merge them into a single record. Do not try to fix it by making one of them v=spf1 include: the other; that is still two records.
3. A syntax slip
SPF parsers are strict. The ones I meet most:
include:_spf.example.com.with a trailing full stop, or a stray space inside a term.ip4:with a CIDR the parser rejects, such asip4:203.0.113.0/33, or an IPv6 address afterip4:.- Two
allterms, or terms afterall(everything after it is ignored by some implementations and an error in others). - A record over 255 characters published as a single string. TXT records are made of strings of at most 255 bytes; a long record must be split into several quoted strings that the receiver joins. Most DNS providers do this for you; a few do not and silently truncate.
- Unknown mechanisms, usually typos:
inlcude:,ipv4:,+mx:.
4. An include that no longer resolves
If include:spf.oldprovider.com points at a name that returns NXDOMAIN, or that exists but has no SPF record, the result of that include is PermError, and PermError propagates up the tree to the whole record. This is the delayed version: it appears the day a provider you stopped paying for deletes their zone, and nobody connects the two events. The tree view marks the failing branch in red.
Finding out which one you have
- Run the email deliverability checker on the domain. The SPF tile shows the lookup count against ten; the tree shows every include expanded, any that failed to resolve, and the terms it could not parse.
- If the tree looks clean, check for a second record with a TXT lookup on the bare domain. The checker warns about this too, but it is worth seeing the raw answer.
- Read one real
Authentication-Resultsheader from a message that reached Gmail or Outlook. It names the result and often the reason:spf=permerror (google.com: domain of example.com uses a mechanism not supported)tells you it is syntax, not lookups.
Fixing it without making it worse
Edit the single record in place; do not add a new one. After every change, re-run the checker and read the count and the tree, not just the grade. The TTL on your TXT record is how long receivers will keep seeing the old version, so if it is a day, the fix takes a day to matter everywhere. If you have to do surgery on a record with many includes, lower the TTL to five minutes first, wait out the old TTL, then make the change.
While SPF is in PermError your mail is standing on DKIM alone. Make sure every sending service has DKIM configured and aligned before you assume the domain is safe at p=reject. SPF vs DKIM vs DMARC covers how the three cover for one another.
A quick reference
| Symptom in the tree | Cause | Fix |
|---|---|---|
| Count shows 11 or more of 10 | Too many lookups | Remove dead includes, use ip4/ip6, split senders onto subdomains |
| Warning: two v=spf1 records | Duplicate record | Merge into one TXT record |
| A term flagged as unparseable | Syntax | Fix the term; check for trailing dots, bad CIDRs, typos |
| An include in red with NXDOMAIN or no record | Dead include | Remove it, or replace it with the provider's current name |