{"record":{"id":"6f94f49cbb485407","repo":"TechnitiumSoftware/DnsServer","slug":"no-soa-record-was-found-for-ixfr","errorCode":null,"errorMessage":"No SOA record was found for IXFR.","messagePattern":"No SOA record was found for IXFR\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs","lineNumber":2531,"sourceCode":"                }\n            }\n\n            //end message\n            xfrRecords.Add(soaRecord);\n\n            return xfrRecords;\n        }\n\n        public IReadOnlyList<DnsResourceRecord> QueryIncrementalZoneTransferRecords(string zoneName, DnsResourceRecord clientSoaRecord)\n        {\n            AuthZoneInfo authZone = GetAuthZoneInfo(zoneName, true);\n            if (authZone is null)\n                throw new InvalidOperationException(\"Zone was not found: \" + zoneName);\n\n            //primary, secondary, forwarder, and catalog zones support zone transfer\n            IReadOnlyList<DnsResourceRecord> soaRecords = authZone.ApexZone.GetRecords(DnsResourceRecordType.SOA);\n            if (soaRecords.Count != 1)\n                throw new InvalidOperationException(\"No SOA record was found for IXFR.\");\n\n            DnsResourceRecord currentSoaRecord = soaRecords[0];\n            uint clientSerial = (clientSoaRecord.RDATA as DnsSOARecordData).Serial;\n\n            if (clientSerial == (currentSoaRecord.RDATA as DnsSOARecordData).Serial)\n            {\n                //zone not modified\n                return [currentSoaRecord];\n            }\n\n            //find history record start from client serial\n            IReadOnlyList<DnsResourceRecord> zoneHistory = authZone.ZoneHistory;\n\n            int index = 0;\n            while (index < zoneHistory.Count)\n            {\n                //check difference sequence\n                if ((zoneHistory[index].RDATA as DnsSOARecordData).Serial == clientSerial)","sourceCodeStart":2513,"sourceCodeEnd":2549,"githubUrl":"https://github.com/TechnitiumSoftware/DnsServer/blob/d0484b6c1e7439cdc53d67d81e9c876cda2ad756/DnsServerCore/Dns/ZoneManagers/AuthZoneManager.cs#L2513-L2549","documentation":"Thrown by QueryIncrementalZoneTransferRecords when the zone exists but apexZone.GetRecords(SOA).Count != 1. IXFR requires exactly one SOA record to determine the current serial and compute incremental diffs. Zones without a valid single SOA (stub, catalog, or corrupted zones) cannot support incremental transfer.","triggerScenarios":"Calling QueryIncrementalZoneTransferRecords on a zone that has zero or multiple SOA records. The zone passes the null check (error 414) but fails the SOA integrity check. This includes catalog zones (which do support IXFR per the comment but only through a different path) and stub zones (no SOA).","commonSituations":"Stub zone queried for IXFR (no SOA); zone corruption causing missing/duplicate SOA; zone in transition (being converted between types); catalog zone edge case where the comment notes catalogs support transfer but SOA check still applies.","solutions":["Verify the zone has exactly one SOA record before requesting IXFR.","For zones without SOA, use full AXFR or the zone-type-specific transfer mechanism.","Repair corrupted zone files to ensure exactly one SOA at the apex.","Check zone type: stub and certain catalog zones do not support IXFR."],"exampleFix":"// before\nvar ixfrRecords = authZoneManager.QueryIncrementalZoneTransferRecords(zoneName, clientSoaRecord);\n\n// after\nAuthZoneInfo info = authZoneManager.GetAuthZoneInfo(zoneName, true);\nif (info is null)\n    throw new InvalidOperationException(\"Zone not found.\");\nvar soaRecords = info.ApexZone.GetRecords(DnsResourceRecordType.SOA);\nif (soaRecords.Count != 1)\n    return authZoneManager.QueryZoneTransferRecords(zoneName); // fall back to AXFR\nvar ixfrRecords = authZoneManager.QueryIncrementalZoneTransferRecords(zoneName, clientSoaRecord);","handlingStrategy":"validation","validationCode":"AuthZoneInfo info = authZoneManager.GetAuthZoneInfo(zoneName, true);\nif (info is null)\n    throw new InvalidOperationException(\"Zone not found.\");\nvar soaRecords = info.ApexZone.GetRecords(DnsResourceRecordType.SOA);\nif (soaRecords.Count != 1)\n    throw new InvalidOperationException($\"Zone has {soaRecords.Count} SOA records; IXFR requires exactly one.\");","typeGuard":"static bool ZoneHasSingleSoa(AuthZoneManager mgr, string zoneName)\n{\n    AuthZoneInfo info = mgr.GetAuthZoneInfo(zoneName);\n    if (info is null) return false;\n    return info.ApexZone.GetRecords(DnsResourceRecordType.SOA).Count == 1;\n}","tryCatchPattern":"try\n{\n    var records = authZoneManager.QueryIncrementalZoneTransferRecords(zoneName, clientSoaRecord);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"No SOA record was found for IXFR\"))\n{\n    logger.LogWarning(\"IXFR not possible for '{Zone}': no single SOA; using AXFR.\", zoneName);\n    records = authZoneManager.QueryZoneTransferRecords(zoneName);\n}","preventionTips":["Verify exactly one SOA record before requesting IXFR.","Fall back to AXFR when SOA integrity is uncertain.","Do not attempt IXFR on zones that don't maintain SOA (stub zones)."],"tags":["zone-transfer","ixfr","zone-management","soa","validation"],"backgroundTag":null,"analyzedSha":"d0484b6c1e7439cdc53d67d81e9c876cda2ad756","analyzedAt":"2026-08-13T22:57:35.508Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}