27 May 2026 · 11 min read
If you have ever opened a DMARC aggregate report and seen the word permerror next to a sender you trust, you have met the single most common reason legitimate mail quietly fails authentication. SPF PermError is not a transient hiccup or a one-off glitch. It is a permanent error: the receiving server tried to evaluate your SPF record, found something structurally wrong with it, and gave up. From that moment, SPF contributes nothing to your DMARC result for every message that hits that error. If you are relying on SPF alignment to reach p=reject, a PermError can stall your entire rollout.
The good news is that PermError is almost always fixable, and the fix is mechanical once you understand what triggers it. This guide walks through the three SPF result classes that cause trouble (PermError, TempError and the dreaded "too many DNS lookups"), explains exactly what each one means, and gives you a concrete, step by step process to diagnose and repair your record. You can check your own record against everything here using the free SPF checker as you read.
What SPF actually returns, and why PermError is special
When a receiving mail server evaluates SPF, it does not simply answer "yes" or "no". The standard (RFC 7208) defines a small set of possible results, and each one carries a different meaning for how the message should be treated:
- Pass: the sending IP is authorised by the domain's SPF record.
- Fail: the sending IP is explicitly not authorised (a
-allmatch). - SoftFail: the IP is probably not authorised, but the domain is not asserting that strongly (a
~allmatch). - Neutral: the domain has explicitly taken no position (
?all). - None: there is no SPF record to evaluate at all.
- TempError: a temporary failure, usually a DNS timeout, that might succeed on retry.
- PermError: a permanent failure caused by an invalid or unprocessable SPF record.
The first five are ordinary outcomes. TempError and PermError are different: they signal that the check could not be completed properly. The distinction between them matters enormously. A TempError is the receiver's problem in the moment and tends to resolve itself. A PermError is your problem, sitting in your DNS, breaking every evaluation until you fix it.
For DMARC, this distinction is brutal. DMARC needs at least one of SPF or DKIM to pass and align. A PermError is not a pass. So if a particular sender only authenticates via SPF (no DKIM signature, or an unaligned one), a PermError means that mail stream has no path to a DMARC pass at all. Once you move to p=quarantine or p=reject, those messages start landing in spam or bouncing.
Cause one: too many DNS lookups (the ten-lookup limit)
The overwhelmingly most common cause of PermError is exceeding the DNS lookup limit. RFC 7208 caps the number of DNS-querying mechanisms an SPF record may trigger at ten. Go over ten and the receiver must return PermError. This is a hard limit designed to stop a single SPF check from hammering DNS with dozens of queries.
The mechanisms that count against this limit are the ones that require a DNS query to resolve:
includeamxptr(deprecated, and you should remove it)exists- the
redirectmodifier
Crucially, include is recursive. Each include you list pulls in another domain's SPF record, and that record may contain its own include statements, each of which costs another lookup. The cost is cumulative across the entire tree, not just the includes you can see in your own record.
Here is a record that looks perfectly innocent:
v=spf1 include:_spf.google.com include:sendgrid.net include:mailgun.org include:_spf.salesforce.com include:servers.mcsv.net -all
Five includes. Surely fine? Not necessarily. Expand each one and you might find:
_spf.google.comresolves to three further includes (_netblocks,_netblocks2,_netblocks3), so it costs four lookups in total.sendgrid.netadds another couple._spf.salesforce.comchains into its own includes.
Add them up and a record with five visible includes can easily resolve to twelve, fifteen, or more actual lookups. The receiver counts every one, hits eleven, and returns PermError. Your record is "valid" in the sense that the syntax is correct, but it is broken in practice.
The free SPF checker expands the full include tree for you and shows the running lookup count at each step, so you can see exactly which include pushed you over the edge. We also go deep on this specific failure in the SPF too many lookups problem.
Fixing the lookup limit
There are several legitimate ways to bring the count back under ten, in rough order of preference:
- Remove senders you no longer use. This sounds obvious, but it is astonishing how often a record still lists a marketing platform, a CRM, or a transactional service that was decommissioned two years ago. Audit your actual mail streams using DMARC aggregate reports, then delete includes for anything that is not sending. Each removed include can claw back several lookups.
- Replace
aandmxmechanisms where you can. Anmxmechanism resolves your MX records and then queries each host, which can be expensive. If you know the fixed IP addresses of your own mail servers, list them withip4:andip6:mechanisms instead. IP mechanisms cost zero DNS lookups.
- Drop
ptrentirely. Theptrmechanism is deprecated, slow, and unreliable. Remove it.
- Flatten the record. SPF flattening means resolving the include tree yourself and replacing the includes with the literal
ip4andip6ranges they expand to. This collapses many lookups into zero. The catch is that providers change their IP ranges without warning, so a static flattened record goes stale and starts failing legitimate mail. If you flatten, you must monitor those source records and re-flatten when they change. We explain the mechanics and the maintenance burden in how SPF flattening works. The hosted SPF service automates this: it keeps the flattened ranges current and serves them through a single managed record, so you stay under ten lookups without manually chasing provider changes.
- Use subdomains to split mail streams. Bulk marketing can send from
news.example.comwith its own SPF record, while transactional mail uses the parent domain. Each subdomain has its own ten-lookup budget. This works only when the visibleFromdomain actually differs, since SPF is evaluated against the domain in the envelope sender (theMAIL FROM), which must still align with the headerFromfor DMARC.
Cause two: syntax errors in the record
The second family of PermErrors comes from malformed records. Even a record well under the lookup limit will PermError if the receiver cannot parse it. The most common syntax problems are:
- Two SPF records on the same domain. A domain must have exactly one record beginning with
v=spf1. If a TXT lookup returns two, the result is PermError. This happens constantly: someone adds a second provider by publishing a whole newv=spf1 ...record rather than adding anincludeto the existing one. Merge them into a single record.
- A typo in the version tag. The record must start with exactly
v=spf1. Variations likev=spf 1,spf1, or a stray leading space break parsing.
- Invalid mechanism syntax. A bare
includewith no domain, a malformedip4block likeip4:192.0.2(missing octets or CIDR), or an unrecognised mechanism token will all trigger PermError.
- A macro or character the parser rejects. SPF supports macros, and a malformed macro expression is a permanent error.
- Multiple
allmechanisms, or text afterall. Anything following the terminalallis ignored at best and a parse problem at worst. Theallshould be last.
A record like this is the classic double-record mistake:
example.com TXT "v=spf1 include:_spf.google.com ~all"
example.com TXT "v=spf1 include:mailgun.org ~all"
Two records, both starting v=spf1. The correct fix is one record:
example.com TXT "v=spf1 include:_spf.google.com include:mailgun.org ~all"
To catch these, run your domain through the SPF checker, which flags multiple records, bad version tags and unparseable mechanisms directly, rather than leaving you to read raw TXT output and guess.
Cause three: void lookups
There is a subtler limit that catches people out. RFC 7208 also caps the number of void lookups at two. A void lookup is a DNS query that returns either an empty answer (NOERROR with no records) or NXDOMAIN. If your record has more than two mechanisms pointing at hostnames that no longer resolve, the evaluation returns PermError even though your total lookup count is fine.
This usually happens when an include references a provider domain that has been retired, or an a/mx mechanism points at a host that has been removed. The fix is the same as the audit above: find the dead references and delete them. Each include for a defunct service is both a wasted lookup and a void-lookup risk.
TempError: the one that is usually not your fault
TempError (sometimes written temperror) is the calmer cousin. It means the receiver hit a transient DNS problem while evaluating your record: a timeout, a server that did not respond, or a temporary resolution failure. Per the standard, the receiver should treat this as a reason to defer rather than reject, so a TempError on an inbound message typically results in a temporary bounce (a 4xx response) and the sending server retries later.
Because TempError is transient, most of the time you do nothing and it clears on the next attempt. But if you see TempError appearing persistently for your domain in DMARC reports, that is a signal worth investigating, because something on your side may be making your SPF record hard or slow to resolve. Likely causes include:
- An authoritative nameserver that is slow or intermittently unreachable. If one of your DNS providers is flaky, lookups time out and present as TempError.
- DNSSEC misconfiguration. A broken DNSSEC chain can cause validating resolvers to fail the lookup temporarily.
- An include pointing at a provider whose own DNS is unreliable. You inherit their resolution problems.
- Overly aggressive rate limiting on your DNS, dropping queries under load.
The remedy is to harden your DNS rather than to touch the SPF record itself: use a reliable, redundant DNS provider, verify your DNSSEC chain is intact, and keep TTLs sensible so caching absorbs bursts. If TempError correlates with a single problematic include, consider whether that provider belongs in your record at all.
A step by step diagnosis and repair process
Here is the workflow we follow when a domain reports SPF authentication failures. Work through it in order.
- Confirm the symptom. Pull your most recent DMARC aggregate data and look for SPF results of
permerrorortemperror, and note which sending sources they attach to. If you do not yet have report parsing in place, the DMARC report analyzer turns the raw XML into a readable breakdown by source and result. Set up ongoing visibility with emailed monitoring so you are alerted the moment a PermError appears.
- Inspect the live record. Run the domain through the SPF checker. Read off three things immediately: how many records exist (must be exactly one), the total DNS lookup count after full include expansion (must be ten or fewer), and any flagged syntax errors.
- If there are two records, merge them. Combine all mechanisms into a single
v=spf1 ... allrecord and republish. Delete the duplicate.
- If the lookup count is over ten, prune first. Remove includes for services you no longer use. This alone fixes a large share of real-world PermErrors and costs nothing.
- Replace expensive mechanisms. Swap
a,mxand anyptrfor literalip4/ip6ranges where you control the IPs. Removeptroutright.
- If you are still over ten, flatten or split. Either flatten the chronically large includes (and commit to keeping them current), or move a mail stream to a subdomain with its own record. See how SPF flattening works for the trade-offs.
- Check void lookups. Remove any include or host reference that no longer resolves.
- Re-test, then watch the reports. After republishing, re-run the SPF checker to confirm a clean parse and an in-budget lookup count. DNS changes take time to propagate, so allow for your TTL. Then watch your DMARC aggregate reports over the next few days to confirm the
permerrorresults have disappeared and the affected sources now show SPFpassand alignment.
Do not forget alignment
A passing SPF result is necessary but not sufficient for DMARC. SPF authenticates the envelope sender domain (MAIL FROM), but DMARC cares about the visible header From. The two must be in the same organisational domain for SPF to count towards a DMARC pass. So you can fix every PermError, get a clean pass, and still see DMARC fail because the SPF domain does not align with the From domain. This is exactly why DKIM matters as a second, more robust path, since DKIM survives forwarding and is generally easier to align. If your senders fail alignment, read DMARC alignment explained and shore up DKIM alongside SPF using the DKIM checker.
The practical takeaway
SPF PermError is a structural fault in your record, not bad luck. Nine times out of ten the cause is the ten-lookup limit, and the other failures are duplicate records, syntax slips, dead references or DNS flakiness presenting as TempError. None of these are mysterious once you can see the expanded record and the running lookup count in front of you.
Start by running your domain through the free SPF checker to see exactly where you stand, then work the diagnosis steps above in order. If your record is permanently fighting the lookup limit because you genuinely send through many providers, keeping it flat, valid and under ten lookups by hand becomes a recurring chore that breaks silently whenever a provider changes its ranges. That is precisely the maintenance the hosted SPF service takes off your plate, and the wider done-for-you platform keeps SPF, DKIM and DMARC aligned and monitored all the way to p=reject.