{"record":{"id":"c6cc6acb3be8ed67","repo":"caddyserver/caddy","slug":"could-not-determine-zone-for-domain-w-domain-s","errorCode":null,"errorMessage":"could not determine zone for domain: %w (domain=%s nameservers=%v)","messagePattern":"could not determine zone for domain: %w \\(domain=(.+?) nameservers=(.+?)\\)","errorType":"exception","errorClass":"PublishECHConfigListErrors","httpStatus":null,"severity":"warning","filePath":"modules/caddytls/ech.go","lineNumber":830,"sourceCode":"// because it is unlikely that specific configuration, such as an API key,\n// is relevant to unique key use as an ECH config publisher.\nfunc (dnsPub ECHDNSPublisher) PublisherKey() string {\n\treturn string(dnsPub.provider.(caddy.Module).CaddyModule().ID)\n}\n\n// PublishECHConfigList publishes the given ECH config list (as binary) to the given DNS names.\n// If there is an error, it may be of type PublishECHConfigListErrors, detailing\n// potentially multiple errors keyed by associated innerName.\nfunc (dnsPub *ECHDNSPublisher) PublishECHConfigList(ctx context.Context, innerNames []string, configListBin []byte) error {\n\tnameservers := certmagic.RecursiveNameservers(nil) // TODO: we could make resolvers configurable\n\n\terrs := make(PublishECHConfigListErrors)\n\nnextName:\n\tfor _, domain := range innerNames {\n\t\tzone, err := certmagic.FindZoneByFQDN(ctx, dnsPub.logger, domain, nameservers)\n\t\tif err != nil {\n\t\t\terrs[domain] = fmt.Errorf(\"could not determine zone for domain: %w (domain=%s nameservers=%v)\", err, domain, nameservers)\n\t\t\tcontinue\n\t\t}\n\n\t\trelName := libdns.RelativeName(domain+\".\", zone)\n\n\t\t// get existing records for this domain; we need to make sure another\n\t\t// record exists for it so we don't accidentally trample a wildcard; we\n\t\t// also want to get any HTTPS record that may already exist for it so\n\t\t// we can augment the ech SvcParamKey with any other existing SvcParams\n\t\trecs, err := dnsPub.provider.GetRecords(ctx, zone)\n\t\tif err != nil {\n\t\t\terrs[domain] = fmt.Errorf(\"unable to get existing DNS records to publish ECH data to HTTPS DNS record: %w\", err)\n\t\t\tcontinue\n\t\t}\n\t\tvar httpsRec libdns.ServiceBinding\n\t\tvar nameHasExistingRecord bool\n\t\tfor _, rec := range recs {\n\t\t\trr := rec.RR()","sourceCodeStart":812,"sourceCodeEnd":848,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/modules/caddytls/ech.go#L812-L848","documentation":"While publishing an HTTPS DNS record carrying ECH data, Caddy must determine the DNS zone (cutting point) for each inner name using certmagic.FindZoneByFQDN against the recursive nameservers (from resolv.conf / defaults). This per-domain error records that SOA discovery failed — DNS query errors, no SOA found up the hierarchy, or unreachable resolvers. It is collected into PublishECHConfigListErrors keyed by domain, not aborting the whole loop.","triggerScenarios":"PublishECHConfigList for an innerName whose zone lookup fails: resolver unreachable (egress blocked on UDP/TCP 53), SERVFAIL from recursive resolvers, name with no SOA (not delegated), or a nameserver list that only resolves internal domains.","commonSituations":"Containers/VMs with broken /etc/resolv.conf; firewall blocking outbound DNS so public SOA lookup fails; trying to publish ECH for a name that is not actually delegated in public DNS; DNSSEC validation failure upstream.","solutions":["Verify the resolver path: dig SOA <domain> from the same host/container; fix resolv.conf or egress rules.","Confirm the domain is delegated and has an SOA in public DNS (dig SOA example.com +trace).","Remove non-public/internal-only names from the ECH-enabled sites, since HTTPS-record publication requires public DNS.","Check the nameservers= list in the error to see which resolvers were used and adjust host resolver config."],"exampleFix":"# before: container blocks outbound DNS\n$ dig SOA example.com\n;; connection timed out\n\n# after: allow DNS egress or point to a working resolver\n$ dig SOA example.com\nexample.com. 3600 IN SOA ns1.example.com. ...","handlingStrategy":"fallback","validationCode":"// Pre-check zone resolvability before enabling ECH publishing.\nfunc zoneResolvable(domain string) error {\n    ns, _ := certmagic.RecursiveNameservers(nil)\n    _, err := certmagic.FindZoneByFQDN(context.Background(), logger, domain, ns)\n    return err\n}","typeGuard":null,"tryCatchPattern":"var perrs caddytls.PublishECHConfigListErrors\nif errors.As(err, &perrs) {\n    for domain, derr := range perrs {\n        if strings.Contains(derr.Error(), \"could not determine zone\") {\n            // skip this domain, keep others; fix resolver/delegation then reload\n        }\n    }\n}","preventionTips":["Ensure outbound DNS (53/udp+tcp) works from the Caddy host/container.","Only enable ECH for publicly delegated domains.","Monitor resolver health (dnsmasq/systemd-resolved) in containers."],"tags":["tls","ech","dns","network","soa"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}