{"record":{"id":"dec720d924591333","repo":"thanos-io/thanos","slug":"maximum-number-of-recursive-iterations-reached-d","errorCode":null,"errorMessage":"maximum number of recursive iterations reached (%d)","messagePattern":"maximum number of recursive iterations reached \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/discovery/dns/miekgdns/resolver.go","lineNumber":29,"sourceCode":"\t\"github.com/pkg/errors\"\n)\n\n// DefaultResolvConfPath is a common, default resolv.conf file present on linux server.\nconst DefaultResolvConfPath = \"/etc/resolv.conf\"\n\n// Resolver is a drop-in Resolver for *part* of std lib Golang net.DefaultResolver methods.\ntype Resolver struct {\n\tResolvConf string\n}\n\nfunc (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) {\n\treturn r.lookupSRV(service, proto, name, 1, 8)\n}\n\nfunc (r *Resolver) lookupSRV(service, proto, name string, currIteration, maxIterations int) (cname string, addrs []*net.SRV, err error) {\n\t// We want to protect from infinite loops when resolving DNS records recursively.\n\tif currIteration > maxIterations {\n\t\treturn \"\", nil, errors.Errorf(\"maximum number of recursive iterations reached (%d)\", maxIterations)\n\t}\n\tvar target string\n\tif service == \"\" && proto == \"\" {\n\t\ttarget = name\n\t} else {\n\t\ttarget = \"_\" + service + \"._\" + proto + \".\" + name\n\t}\n\n\tresponse, err := r.lookupWithSearchPath(target, dns.Type(dns.TypeSRV))\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\tfor _, record := range response.Answer {\n\t\tswitch addr := record.(type) {\n\t\tcase *dns.SRV:\n\t\t\taddrs = append(addrs, &net.SRV{\n\t\t\t\tWeight:   addr.Weight,","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/discovery/dns/miekgdns/resolver.go#L11-L47","documentation":"Guard against infinite recursion: SRV lookups that return CNAME records are followed recursively, capped at maxIterations (8 by default). Exceeding it means the DNS chain is too deep or loops on itself, so lookupSRV aborts rather than spinning forever.","triggerScenarios":"LookupSRV (or internal lookupSRV) follows a chain of more than 8 CNAME/SRV targets: currIteration > maxIterations on a nested call.","commonSituations":"CNAME loops in a DNS zone (target points back at itself); pathologically long CNAME chains; misconfigured service aliases creating a cycle.","solutions":["Inspect the DNS records for the name: dig SRV/CNAME and follow the chain to find the loop","Fix the DNS zone to remove the CNAME loop or shorten the chain","If legitimately deep chains are needed, raise the iteration cap via the exported LookupSRV maxIterations parameter"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// detect CNAME loops before discovery\nseen := map[string]bool{}\nfor t, ok := followCNAME(name); ok; t, ok = followCNAME(t.Target) {\n    if seen[t] { log.Fatalf(\"CNAME loop at %s\", t) }; seen[t] = true\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"maximum number of recursive iterations\") {\n    return fmt.Errorf(\"broken DNS alias chain for %s: %w\", name, err) // fix DNS, do not retry\n}","preventionTips":["Validate DNS zones for CNAME loops before pointing discovery at them","Keep alias chains shallow (1-2 hops max)","Do not create SRV names that CNAME back to themselves or each other","Raise maxIterations only after confirming the chain is legitimately deep"],"tags":["dns","srv","recursion","cname-loop"],"backgroundTag":"dns-cname-loop","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}