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

Extended DNS Errors: the server says why

SERVFAIL is a shrug. An Extended DNS Error (RFC 8914) is that same shrug with a reason attached: a number, a registry name, and a line of plain text riding next to the failure. Read it and you stop guessing.

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:
SERVFAILThe server tried, and could not finish the job.
, id: 16191
;; flags:
qrThis message is the answer coming back, not the question going out.
rdYou asked this server to go and find the answer for you.
raThis server says it is willing to go and find answers for you.
; 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
AYou asked for the IPv4 address this name points at.
;;
Query timeHow long this one lookup took, start to finish.
: 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

good: SERVFAIL and an EDE line naming the reason

bad: 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.

Watch one arrive#

dnssec-failed.org is a zone that is deliberately signed wrong. Ask for it and a validating resolver refuses, then says why. Press run.

run it right here; the full tool has every option

open in the full tool →

  1. Who we asked. Cloudflare (1.1.1.1), reached over DoH from a Cloudflare edge location, with DNSSEC data requested and checking left switched on. Not your machine and not your router: see the dig tool manual on where the query runs from.
  2. What came back. SERVFAIL, zero answer records, and one line that a bare SERVFAIL would never have given you:
;; EDE 9 DNSKEY Missing: no SEP matching the DS found for dnssec-failed.org.
  1. What the number says. 9 is the INFO-CODE. DNSKEY Missing is that code's name in the IANA registry, printed for you rather than looked up. Both come off the wire.
  2. What the text says. no SEP matching the DS found is Cloudflare's EXTRA-TEXT, and it names the exact broken joint. A SEP is a Secure Entry Point, the DNSKEY flagged as the key a parent is allowed to point at (RFC 4034 section 2.1.1). The parent zone org publishes a DS record that fingerprints one key. The child zone serves a different one.
  3. Verdict. The chain is broken at the org to dnssec-failed.org delegation: the DS in the parent and the DNSKEY set in the child do not agree. EDE 9 says they disagree, not which side moved, and you cannot tell from one query whether the child rolled its key or the parent published the wrong digest. Nobody had to run a second query to learn that. Fixing that class of break is the DNSSEC field guide; this page stops at reading the line.

Two resolvers, one zone, two codes#

Ask a different validator and you can get a different code for the same broken zone. Quad9, same name, same moment (captured 2026-08-25):

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 512
; EDE: 7 (Signature Expired)

EDE 7, and no EXTRA-TEXT at all. Cloudflare stopped at the missing key; Quad9 walked further and hit a dead signature first. Neither is lying. 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. dnssec-failed.org re-signs on its own schedule, so run it yourself and expect your own codes.

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-04 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

Five of them cover most of what an operator meets in a bad week. 6 says validation failed and gave no detail. 9 and 10 say the signing is wrong in a specific way. 22 and 23 say the resolver never got a usable answer to validate, which is a reachability problem wearing a DNSSEC costume.

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. 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. There is no flag that turns the display on: +ede and +noede are rejected as invalid options on DiG 9.20.24, and the only relevant switch is +noedns, which removes the OPT from the query and takes the answer's EDE with it.

dig +https @1.1.1.1 +dnssec dnssec-failed.org A
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 42642
;; 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.)

+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. Drop it once you trust your own path, and keep it whenever the answer is the evidence.

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. Quoting that string into a ticket beats describing the failure in your own words, every time.

See also#