SPF does not fail loudly when it gets too complicated. The record stays published, the DNS zone looks fine, and mail starts failing because the receiver stopped evaluating after ten lookups and returned permerror.
Nothing was deleted. Nothing was renamed. An include was added for a new tool, and the chain crossed a line that was already close.
What counts
RFC 7208 limits an SPF evaluation to 10 DNS-querying mechanisms and modifiers. The ones that count are:
includeamxptr(deprecated, and best removed)existsredirect
Each include is one query, and then the included record's own mechanisms are evaluated too. That is where the chain grows: a provider's record can contain four includes of its own, and those children count against your ten.
There is a second limit: two void lookups — queries that return no answer. A typo in an include domain can consume those before the real records are reached.
ip4, ip6, all, exp and plain text do not count. Adding an IP address is cheap for the limit, but expensive for maintenance when the provider changes ranges.
How to count by hand
Start from the record and walk every include and redirect, counting each one. Stop at ten — if you are close, assume a new tool will push it over.
v=spf1 include:_spf.google.com include:servers.mcsv.net
include:sendgrid.net include:mail.zendesk.com ~all
That looks like four lookups. If _spf.google.com contains three includes, servers.mcsv.net two and sendgrid.net one, the real count is ten before the record has even finished. Add a fifth tool and every check returns permerror.
Do not trust a count that only looks at the top-level record. The nested records are where the limit is spent.
The symptom
A receiver records the result in Authentication-Results:
spf=permerror smtp.mailfrom=client.example
permerror is permanent for that evaluation: the receiver will not retry it. If DMARC relies on SPF alignment and DKIM is not signing the From domain, the message fails DMARC.
This is also why a record can work in one checker and fail in another: the checker may stop at the top level, use a cached answer, or evaluate a different envelope domain.