{"record":{"id":"2510424a3d304000","repo":"coredns/coredns","slug":"no-soa-rrsig-found-in-first-100-records","errorCode":null,"errorMessage":"no SOA RRSIG found in first 100 records","messagePattern":"no SOA RRSIG found in first 100 records","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/sign/signer.go","lineNumber":160,"sourceCode":"\t\t\tif now.Sub(incep) >= 0 && now.Sub(incep) > durationResignDays {\n\t\t\t\treturn fmt.Errorf(\"inception %q was more than: %s ago from %s: %s\", incep.Format(timeFmt), durationResignDays, now.Format(timeFmt), now.Sub(incep))\n\t\t\t}\n\t\t\t// Inception hasn't even start yet.\n\t\t\tif now.Sub(incep) < 0 {\n\t\t\t\treturn fmt.Errorf(\"inception %q date is in the future: %s\", incep.Format(timeFmt), now.Sub(incep))\n\t\t\t}\n\n\t\t\texpire, _ := time.Parse(\"20060102150405\", dns.TimeToString(x.Expiration))\n\t\t\tif expire.Sub(now) < durationExpireDays {\n\t\t\t\treturn fmt.Errorf(\"expiration %q is less than: %s away from %s: %s\", expire.Format(timeFmt), durationExpireDays, now.Format(timeFmt), expire.Sub(now))\n\t\t\t}\n\t\t}\n\t\ti++\n\t\tif i > 100 {\n\t\t\t// 100 is a random number. A SOA record should be the first in the zonefile, but RFC 1035 doesn't actually mandate this. So it could\n\t\t\t// be 3rd or even later. The number 100 looks crazy high enough that it will catch all weird zones, but not high enough to keep the CPU\n\t\t\t// busy with parsing all the time.\n\t\t\treturn fmt.Errorf(\"no SOA RRSIG found in first 100 records\")\n\t\t}\n\t}\n\n\treturn zp.Err()\n}\n\nfunc signAndLog(s *Signer, why error) {\n\tnow := time.Now().UTC()\n\tz, err := s.Sign(now)\n\tlog.Infof(\"Signing %q because %s\", s.origin, why)\n\tif err != nil {\n\t\tlog.Warningf(\"Error signing %q with key tags %q in %s: %s, next: %s\", s.origin, keyTag(s.keys), time.Since(now), err, now.Add(durationRefreshHours).Format(timeFmt))\n\t\treturn\n\t}\n\n\tif err := s.write(z); err != nil {\n\t\tlog.Warningf(\"Error signing %q: failed to move zone file into place: %s\", s.origin, err)\n\t\treturn","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/coredns/coredns/blob/558c9757a92b361e550bdba82d0b4ddf3d12d595/plugin/sign/signer.go#L142-L178","documentation":"resign() scans the first 100 records of a signed zone looking for the RRSIG covering the SOA record. If none is found within that limit it gives up and returns this error, because the plugin cannot determine signature freshness for the zone's SOA.","triggerScenarios":"resign() is called on a zone whose first 100 records contain no RRSIG with TypeCovered == dns.TypeSOA (no SOA RRSIG at all, or SOA pushed beyond the 100th record).","commonSituations":"Serving an unsigned zone file through the sign plugin's resign check path; a malformed/unsorted zone file with the SOA buried past 100 records; hand-edited zone missing its SOA RRSIG.","solutions":["Re-sign the zone so an SOA RRSIG exists (or sign it at all)","Move the SOA record and its RRSIG to the top of the zone file (RFC 1035 convention)","Validate the zone file with named-checkzone or dnssec-verify","Ensure the correct zone file is loaded"],"exampleFix":"// before (SOA RRSIG missing)\nexample.org. 3600 IN SOA ns1 ...\nexample.org. 3600 IN NS ns1 ...\n// after: re-sign so the SOA carries an RRSIG\nexample.org. 3600 IN RRSIG SOA 8 2 3600 ... (sig)\nexample.org. 3600 IN SOA ns1 ...","handlingStrategy":"validation","validationCode":"func hasSOARRSIG(z *zone.File) bool {\n\tfor _, rr := range z.RR {\n\t\tif r, ok := rr.(*dns.RRSIG); ok && r.TypeCovered == dns.TypeSOA {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}","typeGuard":null,"tryCatchPattern":"if err := resign(zoneFile); err != nil {\n\tif strings.Contains(err.Error(), \"no SOA RRSIG\") {\n\t\tlog.Printf(\"zone not signed or malformed: %v\", err)\n\t\t// run full sign instead of resign check\n\t}\n}","preventionTips":["Always place the SOA record (with its RRSIG) at the top of the zone file","Run dnssec-verify on zone files before serving","Never hand-edit or strip RRSIG records from signed zones","Validate zones with named-checkzone as part of CI"],"tags":["dns","dnssec","zone-file","soa"],"backgroundTag":"record-not-found","analyzedSha":"558c9757a92b361e550bdba82d0b4ddf3d12d595","analyzedAt":"2026-09-06T22:47:02.106Z","contentChangedAt":"2026-09-06T22:47:02.106Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}