isitdns? wiki/Security/Extended DNS Errors: the server says why
Security

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 A
; <<>> DiG 9.20.11-0ubuntu0.2-Ubuntu <<>> +https @1.1.1.1 +dnssec dnssec-failed.org A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 16191
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 1232
; EDE: 9 (DNSKEY Missing): (no SEP matching the DS found for dnssec-failed.org.)
;; QUESTION SECTION:
;dnssec-failed.org. IN A
;; Query time: 48 msec
;; SERVER: 1.1.1.1#443(1.1.1.1) (HTTPS)
;; WHEN: Mon Sep 14 17:42:31 CDT 2026
;; MSG SIZE rcvd: 103

GO SERVFAIL and an EDE line naming the reason

NO GO SERVFAIL with no EDE, which leaves you guessing why

what each part of this output means →

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.

CodeNameWhat it is telling you
0Other ErrorThe server has a reason with no code of its own. The EXTRA-TEXT is the whole message
1Unsupported DNSKEY AlgorithmThe zone signs with an algorithm this resolver does not implement
2Unsupported DS Digest TypeThe parent's DS uses a hash this resolver does not implement
3Stale AnswerServed from cache past its TTL because the authoritative servers were unreachable (RFC 8767)
4Forged AnswerThe answer was manufactured by filtering policy rather than read from a zone
5DNSSEC IndeterminateThe resolver could not decide whether the answer is correctly signed
6DNSSEC BogusValidation ran and failed. The signatures do not check out
7Signature ExpiredThe RRSIG covering the answer is past its expiration timestamp
8Signature Not Yet ValidThe RRSIG inception is in the future. Suspect a clock, on either end
9DNSKEY MissingThe parent published a DS and no key in the child zone matches it
10RRSIGs MissingThe zone is supposed to be signed and the records arrived unsigned
11No Zone Key Bit SetA DNSKEY is present but is not flagged as a zone key, so it cannot sign
12NSEC MissingA "does not exist" arrived with no proof of non-existence attached
13Cached ErrorThis failure is a stored copy of an earlier one, not a fresh attempt
14Not ReadyThe server is running but is not yet serving this zone
15BlockedThe server refuses this name under its own policy
16CensoredAn external requirement forces the server to withhold the answer
17FilteredThe client asked for this name to be blocked, via a filtering profile
18ProhibitedThe client is not permitted to query this server. An access list, not a zone fault
19Stale NXDomain AnswerA cached "does not exist" served past its TTL because upstream was unreachable
20Not AuthoritativeAn authoritative question about a zone this server does not serve
21Not SupportedThe operation asked for is not implemented here
22No Reachable AuthorityNone of the zone's authoritative servers answered. Look at the auth tier, not the zone data
23Network ErrorThe path to the upstream broke: reset, timeout, unreachable
24Invalid DataThe upstream reply was malformed past the point of use
25Signature Expired before ValidExpiration precedes inception. The RRSIG window is inside out
26Too EarlyA 0-RTT early-data query was rejected and needs sending again after the handshake
27Unsupported NSEC3 Iterations ValueThe zone's NSEC3 iteration count is higher than the resolver will spend CPU on (RFC 9276)
28Unable to conform to policyThe resolver could not meet the client's stated policy for the answer
29SynthesizedThe answer was generated from a rule, DNS64 style, rather than read from a zone
30Invalid Query TypeThe query type is one this server will not process
31Rate LimitedThe client is asking faster than this server will answer
32Over QuotaThe client has spent an allowance the operator set, not a per-second rate
33Negative Trust AnchorValidation was deliberately switched off for this name by the operator, to ride out someone else's broken zone
34New Delegation OnlyThe 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)
35Blocked by Upstream DNS ServerSomething 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 SERVFAIL and nothing else.
  • The query carried no EDNS. No OPT going out, no OPT coming back. dig +noedns proves it in one line, below.
  • Your dig is too old to print it. BIND dig began printing the EDE: line at 9.16.4 (#5408). Older builds receive the option and say nothing about it.
  • nslookup cannot show it. It has no EDNS display at all. Neither does dig +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 ignores Comment shows you a bare Status.
  • 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: 0

ADDITIONAL: 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#