You’re reading your DMARC aggregate report and something’s wrong with SPF. The result column doesn’t say pass or fail, it says permerror or temperror. Your DMARC policy is set to p=quarantine, and you need to know whether this is a crisis or background noise.
The answer depends entirely on which error you’re looking at.
The short version
temperror means the receiving server couldn’t evaluate your SPF record due to a transient DNS problem: a timeout, a SERVFAIL, a resolver that briefly went unresponsive. It usually clears on its own with the next retry.
permerror means the receiving server tried to evaluate your SPF record and found something fundamentally broken, a structural defect it cannot work around. It won’t fix itself. Every message from the affected sender keeps failing until you change the record.
If you’re seeing permerror in a significant fraction of your report rows, stop reading and go fix your SPF. The rest of this post explains exactly what to look for.
What the RFCs actually say
Both error types are defined in RFC 7208, the SPF specification.
temperror (§2.6.6): “the SPF verifier encountered a transient (generally DNS) error while performing the check. A later retry may succeed without further DNS operator action.” The record itself may be perfectly valid; the evaluator just couldn’t reach the DNS infrastructure needed to finish the check.
permerror (§2.6.7): “The domain’s published records could not be correctly interpreted.” Examples include hitting the 10-lookup limit, encountering multiple SPF records on the same name, or finding a syntax error the evaluator cannot parse.
RFC 9989 doesn’t give SPF temperror and permerror special treatment either: a broken SPF check simply gives DMARC no SPF-authenticated identifier to align (§5.3.3, §5.3.5), so the message passes only if an aligned DKIM signature does. §5.3.6 adds that if DNS queries required for DMARC validation don’t complete due to temporary or permanent DNS errors, the message “cannot be considered to pass or fail” and handling is left to the receiver. The RFC doesn’t say whether that covers an SPF lookup failure, so receiver behavior varies. Treating an isolated temperror as noise and a standing permerror as a real failure is operational convention, not an RFC rule.
Where these errors appear in aggregate report XML
DMARC aggregate reports encode authentication results inside <record> elements. You’ll find SPF results here:
<auth_results>
<spf>
<domain>example.com</domain>
<result>permerror</result>
</spf>
</auth_results>
If the result is permerror, the row’s DMARC disposition depends on whether DKIM passed and aligned. If DKIM also failed, the message failed DMARC entirely. If DKIM passed with alignment, DMARC still passes, but SPF is a broken path for that sender.
On the Reports tab in DMARC Guardian, set the SPF result filter to permerror to list the affected rows and their source IPs.
The most common permerror traps
1. The 10-lookup limit
SPF evaluation can trigger at most 10 DNS lookups, across every include, a, mx, ptr, and exists mechanism plus the redirect modifier, recursively (RFC 7208 §4.6.4). Hit lookup 11 and the evaluator returns permerror.
This is the single most common cause of permerror in production. See Adding one SaaS tool broke your SPF record for how to count your lookups and fix it with SPF flattening. No point reproducing it here.
2. Two SPF records on the same name
This one silently breaks authentication for the entire domain and is surprisingly easy to do. Per RFC 7208 §3.2, a domain “MUST NOT have multiple records that would cause an authorization check to select more than one record.” If your DNS zone has two TXT records at the same name that both start with v=spf1, evaluating servers return permerror immediately:
example.com TXT "v=spf1 include:_spf.google.com ~all"
example.com TXT "v=spf1 include:sendgrid.net ~all"
This is usually the result of a well-meaning addition. Someone already had an SPF record. A developer or IT admin added a new service and published a second SPF record instead of editing the existing one. Now both records are broken, not just the new one.
The fix: merge them into a single record:
example.com TXT "v=spf1 include:_spf.google.com include:sendgrid.net ~all"
Check for this with:
dig TXT example.com +short | grep "v=spf1"
If you get two lines back, that’s the problem.
3. Syntax errors
Less common but still real: a typo in a mechanism token, a malformed IP range (ip4: with an invalid CIDR), or an unknown mechanism the evaluator doesn’t recognize. These produce permerror immediately during parsing.
v=spf1 include:_spf.google.com ip4:10.0.0/24 ~all # invalid CIDR — missing bits
v=spf1 includes:_spf.google.com ~all # "includes" is not a mechanism
Run your record through a parser before publishing changes. A dig TXT example.com +short and a visual pass catches most of these.
When temperror becomes a real problem
A single temperror is noise. Every DNS infrastructure has occasional hiccups, and the receiver’s retry usually succeeds.
What you want to watch is patterns:
temperrorappearing across many report senders (Google, Yahoo, Microsoft all returning it): your authoritative DNS server had a period of downtime, or your DNS provider had an incident.temperrorconcentrated at a specific subdomain used in your SPF chain: that third-party provider has flaky DNS on their SPF subdomain (_spf.provider.example.netresolving unreliably).temperrorrecurring regularly, week over week: your DNS hosting is unreliable.
For the first case, review your DNS provider’s status history. If you’re on a free or budget DNS tier and seeing recurring temperror that’s measurably hurting your pass rate, it’s worth evaluating a higher-reliability DNS host.
For the third-party case, you can’t fix their DNS, but you can reduce your dependency on it. SPF flattening resolves include: chains down to IP addresses, removing recursive DNS lookups for those providers. If a provider’s DNS goes down, your flattened record, which has their IPs as ip4: ranges, still evaluates cleanly. That’s a secondary benefit of flattening beyond the lookup-count problem: see Adding one SaaS tool broke your SPF record for the full flattening workflow.
If the errors you’re seeing aren’t SPF-related at all, but low-volume fail rows from IPs you don’t recognize, that’s a different pattern worth ruling out: see Why forwarded email fails SPF, and DKIM saves it.
Which sender is triggering it?
Both permerror and temperror in your aggregate reports are tied to specific source IPs. The workflow for isolating the problem:
- Open your aggregate report for the period in question.
- Filter for rows where SPF result is
permerrorortemperror. - Note the source IP. This is the server that sent the message.
- Map the IP to a service (reverse-DNS lookup, or cross-reference with your sender list).
- For
permerror: that service’s sending path is hitting a broken SPF record. Check which domain’s record is malformed. - For
temperror: that DNS resolver or that provider’s DNS had a transient problem at that time.
The source IP is the critical data point. permerror on traffic from 203.0.113.10 (a Google Workspace-attributed range) tells you something very different from permerror on traffic from 198.51.100.20 (a SendGrid-attributed range).
The decision tree
Is this permerror or temperror?
├── temperror
│ ├── Isolated incident → monitor, no action needed
│ └── Recurring pattern across multiple reporters
│ ├── DNS provider downtime → review provider reliability
│ └── Third-party SPF DNS flaky → consider SPF flattening
└── permerror
├── Run: dig TXT example.com +short | grep "v=spf1" | wc -l
│ └── Result is 2 → duplicate SPF records → merge immediately
├── Count your DNS lookups (see /blog/spf-too-many-dns-lookups)
│ └── Count > 10 → flatten (10 is the maximum allowed) your SPF record
└── Review syntax → fix the malformed mechanism
Checking your current state
Before making changes, get a clean read of your current SPF record:
# Check for duplicate SPF records
dig TXT example.com +short | grep "v=spf1"
# Count lookup-triggering mechanisms (rough estimate: ignores bare `a`/`mx` and `redirect=`)
dig TXT example.com +short | grep -oE '(include|a|mx|exists|ptr):[^[:space:]]+' | wc -l
A tool that fully expands SPF lookups shows the same view: every include: resolved down through the nested tree, with the total lookup count included, which the plain dig command alone misses.
If permerror is showing up consistently in your reports and you haven’t fixed SPF yet, every day you wait is a day some fraction of your legitimate mail fails DMARC and gets quarantined or rejected. The fix is rarely complex, usually a merge of duplicate records or an SPF flattening job, but it has to be done deliberately or it breaks other senders.
Check whether your SPF is over the 10-lookup limit, free, no signup →