Knowledge base

reject-spf-check-permerror

<source>

Whether to reject a message when the sender's SPF record is unreadable
in the configuration this is a linereject-spf-check-permerror trueinside the block <source>
The sender did not forbid anything — it made a mistake

A permanent check error does not mean “sending is not allowed” but “the record could not be parsed”: a syntax error, an invalid construct, too many nested lookups. The default is to reject such a message with a permanent refusal.

You reject because of someone else’s typo. The domain owner forbade nothing; they simply made a mistake when publishing the record. There are more messages from such domains than it seems: complex records with nested references break often and without the owner noticing.

Exceeding the limit on empty DNS responses leads here too — it is controlled by check-spf-inbound-max-void-lookups, and its default is small.

The main trap is a refusal for a reason the sender does not know about. They will not see their mistake: their mail gets through at most recipients, because not everyone is willing to reject over an unreadable record. With you it will stop arriving, and it is you who will have to sort it out, not them.
What the Hub does. It does not write it: inbound checking is not enabled. If it gets enabled, it is wiser to record this outcome in a header than to reject.
Recommended values

It makes more sense not to reject. The result will end up in a header anyway, and the decision can be made further down the chain — already knowing the message contents.

Rejecting makes sense with a stream of forgeries. An unreadable record for a domain that dozens of messages a minute claim to come from is a signal in itself.

Check the empty response limit. Some of these errors come not from the sender’s record but from your own threshold.

The “it makes more sense not to reject” assessment is ours. From the manual here come the default, the refusal code and an example cause: a syntax error in the record.
Example configuration
# we record someone else's typo in a header but accept the message
<source 0/0>
    check-spf-inbound true
    reject-spf-check-fail true
    reject-spf-check-permerror false</source>
The line above rejects what the domain forbade itself; this one — what it got wrong. The decisions differ because the cases differ.
yellow — — directive names, blue — blocks. They are highlighted the same way in the text of the card.
▲

Common mistakes and problems

what people get wrong and how it ends2 mistakes▶

Rejecting on a par with an explicit prohibition

our experience

What it costsMail is rejected from domains whose owners simply made a mistake in the record. At most recipients their messages get through, and it is you who will have to deal with the loss.
How to spot itThe refusals list domains with sloppy rather than forged records.
How to do it rightKeep the result in a header and make the decision further along.

Not connecting this outcome with the empty response limit

§ 3.3.7.24

What it costsSome permanent errors are produced not by the sender’s record but by the node’s own threshold. By tightening the threshold you increase the number of refusals without changing a single line about them.
How to spot itThe share of such outcomes grew after the limit was edited.
How to do it rightLook at both lines together.
Compatibility
PowerMTA 6.0
described in the manual of this version · platform: any
The vendor does not state the first version for every directive — there is no note here. The name also appears in the 5.0 edition of the manual.
Reference data
Syntax
reject-spf-check-permerror <boolean>
Values
yes or no; true and false are accepted on a par with them
Default
true § 3.3.7.70
Applying
reload, no restart needed
Queue
will not touch what is already queued
Where to write it
<source>
How many times
once in the block
Everything here comes from the manual, with the section.
Inheritance
The value is set in a <source> block and applies to mail submitted through a matching source; with several matching blocks the most specific wins. It triggers only with check-spf-inbound enabled. It is picked up by pmta reload.
How to check on the node
The value is set in the source configuration; how many such outcomes arrive is visible from the headers of accepted messages:
grep -n reject-spf-check-permerror /etc/pmta/config
grep -ci 'permerror' /var/log/pmta/pmta.log
A noticeable share of such outcomes usually means not a stream of forgeries but other people’s broken records. The Hub does not write this line.
In the Hub
Cost of a mistake
dangerous: effects outlast a rollback
Scenarios
Bounce classification · Receiving inbound mail