Extended DNS Errors: the server says why
Asks a broken zone for an address and reads the reason the server attached.
dig +https @1.1.1.1 +dnssec dnssec-failed.org AGO SERVFAIL and an EDE line naming the reason
NO GO SERVFAIL with no EDE, which leaves you guessing why
What an EDE is#
An EDE is an EDNS(0) option, option code 15, carried in the OPT record of a response. Inside it are a two-byte INFO-CODE and an optional UTF-8 EXTRA-TEXT string (RFC 8914 section 2). That is the whole format.
It does not change the rcode. SERVFAIL stays SERVFAIL and NOERROR stays NOERROR; the EDE sits beside the rcode and explains it. That matters for the codes that ride a successful answer: EDE 3 (Stale Answer) and EDE 29 (Synthesized) both arrive on a NOERROR you were happy with, and both are telling you the answer did not come from where you assumed.
One response can carry several. A resolver that cached someone else's failure often sends EDE 13 (Cached Error) wrapped around the original validation code, so read every line, not the first one.
EXTRA-TEXT is optional and free-form. When a server sends it you get a sentence; when it does not, you get a bare number and a name. Both are valid.
The code table#
Codes 0 to 24 are defined in RFC 8914 itself. Codes 25 and above were registered afterwards, several of them from drafts and vendor pull requests rather than finished RFCs, so the IANA extended DNS error codes registry is the live list. As of 2026-09-23 it assigns through 35; 36 onward is unassigned.
| Code | Name | What it is telling you |
|---|---|---|
| 0 | Other Error | The server has a reason with no code of its own. The EXTRA-TEXT is the whole message |
| 1 | Unsupported DNSKEY Algorithm | The zone signs with an algorithm this resolver does not implement |
| 2 | Unsupported DS Digest Type | The parent's DS uses a hash this resolver does not implement |
| 3 | Stale Answer | Served from cache past its TTL because the authoritative servers were unreachable (RFC 8767) |
| 4 | Forged Answer | The answer was manufactured by filtering policy rather than read from a zone |
| 5 | DNSSEC Indeterminate | The resolver could not decide whether the answer is correctly signed |
| 6 | DNSSEC Bogus | Validation ran and failed. The signatures do not check out |
| 7 | Signature Expired | The RRSIG covering the answer is past its expiration timestamp |
| 8 | Signature Not Yet Valid | The RRSIG inception is in the future. Suspect a clock, on either end |
| 9 | DNSKEY Missing | The parent published a DS and no key in the child zone matches it |
| 10 | RRSIGs Missing | The zone is supposed to be signed and the records arrived unsigned |
| 11 | No Zone Key Bit Set | A DNSKEY is present but is not flagged as a zone key, so it cannot sign |
| 12 | NSEC Missing | A "does not exist" arrived with no proof of non-existence attached |
| 13 | Cached Error | This failure is a stored copy of an earlier one, not a fresh attempt |
| 14 | Not Ready | The server is running but is not yet serving this zone |
| 15 | Blocked | The server refuses this name under its own policy |
| 16 | Censored | An external requirement forces the server to withhold the answer |
| 17 | Filtered | The client asked for this name to be blocked, via a filtering profile |
| 18 | Prohibited | The client is not permitted to query this server. An access list, not a zone fault |
| 19 | Stale NXDomain Answer | A cached "does not exist" served past its TTL because upstream was unreachable |
| 20 | Not Authoritative | An authoritative question about a zone this server does not serve |
| 21 | Not Supported | The operation asked for is not implemented here |
| 22 | No Reachable Authority | None of the zone's authoritative servers answered. Look at the auth tier, not the zone data |
| 23 | Network Error | The path to the upstream broke: reset, timeout, unreachable |
| 24 | Invalid Data | The upstream reply was malformed past the point of use |
| 25 | Signature Expired before Valid | Expiration precedes inception. The RRSIG window is inside out |
| 26 | Too Early | A 0-RTT early-data query was rejected and needs sending again after the handshake |
| 27 | Unsupported NSEC3 Iterations Value | The zone's NSEC3 iteration count is higher than the resolver will spend CPU on (RFC 9276) |
| 28 | Unable to conform to policy | The resolver could not meet the client's stated policy for the answer |
| 29 | Synthesized | The answer was generated from a rule, DNS64 style, rather than read from a zone |
| 30 | Invalid Query Type | The query type is one this server will not process |
| 31 | Rate Limited | The client is asking faster than this server will answer |
| 32 | Over Quota | The client has spent an allowance the operator set, not a per-second rate |
| 33 | Negative Trust Anchor | Validation was deliberately switched off for this name by the operator, to ride out someone else's broken zone |
| 34 | New Delegation Only | The delegation exists only in the new DELEG form, with no NS records; sent by a DELEG-aware authoritative to a resolver that does not understand DELEG (draft-ietf-deleg) |
| 35 | Blocked by Upstream DNS Server | Something further up the chain blocked it, and this server is relaying that fact rather than owning it |
Gotchas#
Resolvers differ in which code they send for the same break. Cloudflare (1.1.1.1) reports EDE 9 for dnssec-failed.org in the capture above (2026-09-14), and still did over its DoH JSON API on 2026-09-23 at 16:32Z. Quad9 (9.9.9.9), same zone, over DoH from this vantage on 2026-09-23:
$ dig +https @9.9.9.9 +dnssec dnssec-failed.org A
; EDE: 7 (Signature Expired)EDE 7, no EXTRA-TEXT. Cloudflare stopped at the missing key; Quad9 walked further and hit a dead signature first. An EDE reports the first thing that resolver tripped over, so it is evidence about one validator's path, not a verdict on the zone.
When there is no EDE to read#
The absence of an EDE is not the absence of a cause. These are the reasons a real failure comes back bare:
- The resolver does not send them. EDE is optional. Plenty of ISP and appliance resolvers still answer
SERVFAILand nothing else. - The query carried no EDNS. No OPT going out, no OPT coming back.
dig +noednsproves it in one line, below. - Your
digis too old to print it. BINDdigbegan printing theEDE:line at 9.16.4 (#5408). Older builds receive the option and say nothing about it. nslookupcannot show it. It has no EDNS display at all. Neither doesdig +short, which prints nothing whatsoever on a failure.- Something on the path answered instead. An intercepting box on port 53 writes its own reply, and its reply has no reason to carry anyone else's EDE. Query over DoH or DoT when you need the resolver's own words (the encrypted transports).
- The tool path has nowhere to put it. A JSON API loses whatever its schema has no field for. Cloudflare's DoH JSON parks the string in
Comment, and a client that ignoresCommentshows you a bareStatus. - It came from an authoritative server. Most EDEs you meet are minted by a validating recursive resolver, because that is where validation happens. An authoritative server has less to report.
Losing the OPT is the one you can demonstrate on demand. Same resolver, same broken zone, EDNS switched off:
dig +https @1.1.1.1 +noedns dnssec-failed.org A;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 20304
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0ADDITIONAL: 0. No OPT record, so no option 15, so no reason. The failure is identical and the explanation is gone.
Reading one from the command line#
dig prints the EDE whenever the response carries the option, with no flag to turn the display on or off: +ede and +noede are rejected as invalid options (verified on DiG 9.20.18). The only relevant switch is +noedns, which removes the OPT from the query and takes the answer's EDE with it. +https sends the query over DoH (RFC 8484) so that nothing on your local port-53 path can answer in Cloudflare's place; it needs BIND dig 9.18 or newer. The capture at the top of this page is exactly that command; drop +https once you trust your own path.
kdig from the Knot utilities prints the same option, quoting the EXTRA-TEXT instead of bracketing it:
kdig +https @1.1.1.1 +dnssec dnssec-failed.org A;; EDNS PSEUDOSECTION:
;; Version: 0; flags: do; UDP size: 1232 B; ext-rcode: NOERROR
;; EDE: 9 (DNSKEY Missing): 'no SEP matching the DS found for dnssec-failed.org.'When the local dig is too old to print the line, ask over DoH JSON and read Comment:
curl -s -H 'accept: application/dns-json' \
'https://cloudflare-dns.com/dns-query?name=dnssec-failed.org&type=A'{"Status":2,"AD":false,
"Comment":["EDE(9): DNSKEY Missing no SEP matching the DS found for dnssec-failed.org."]}Status 2 is SERVFAIL. The Comment array is where Cloudflare puts the EDE string, code and name and text run together into one line.
See also#
- What does SERVFAIL mean?: the rcode these codes are explaining, and the five causes told apart one dig at a time
- DNSSEC troubleshooting: what to do once the EDE has named the break, with the triage flow and the operational checklist
- DNSSEC, signed and validated: how the trust chain is built, so
EDE 9andEDE 10mean something concrete - The DNSSEC chain, drawn: the same broken zone as a picture, with one verdict line
- The isitdns dig tool: the manual for the site's dig widget, including what it can and cannot see
- dig flags:
+dnssec,+cd,+noednsand every other switch used here - Glossary and RFC index: the one-line definition, and RFC 8914 in the shelf