thanos-io/thanos · error
maximum number of recursive iterations reached
Error message
maximum number of recursive iterations reached (%d)
What it means
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.
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
Defensive patterns
Strategy: validation
Validate before calling
// detect CNAME loops before discovery
seen := map[string]bool{}
for t, ok := followCNAME(name); ok; t, ok = followCNAME(t.Target) {
if seen[t] { log.Fatalf("CNAME loop at %s", t) }; seen[t] = true
} Try / catch
if err != nil && strings.Contains(err.Error(), "maximum number of recursive iterations") {
return fmt.Errorf("broken DNS alias chain for %s: %w", name, err) // fix DNS, do not retry
} Prevention
- 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
When it happens
Trigger: LookupSRV (or internal lookupSRV) follows a chain of more than 8 CNAME/SRV targets: currIteration > maxIterations on a nested call.
Common situations: CNAME loops in a DNS zone (target points back at itself); pathologically long CNAME chains; misconfigured service aliases creating a cycle.
Related errors
- recursively resolve
- invalid SRV response record
- lookup SRV records
- no such host
- could not load resolv.conf
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/dec720d924591333.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/discovery/dns/miekgdns/resolver.go:29
"github.com/pkg/errors"
)
// DefaultResolvConfPath is a common, default resolv.conf file present on linux server.
const DefaultResolvConfPath = "/etc/resolv.conf"
// Resolver is a drop-in Resolver for *part* of std lib Golang net.DefaultResolver methods.
type Resolver struct {
ResolvConf string
}
func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*net.SRV, err error) {
return r.lookupSRV(service, proto, name, 1, 8)
}
func (r *Resolver) lookupSRV(service, proto, name string, currIteration, maxIterations int) (cname string, addrs []*net.SRV, err error) {
// We want to protect from infinite loops when resolving DNS records recursively.
if currIteration > maxIterations {
return "", nil, errors.Errorf("maximum number of recursive iterations reached (%d)", maxIterations)
}
var target string
if service == "" && proto == "" {
target = name
} else {
target = "_" + service + "._" + proto + "." + name
}
response, err := r.lookupWithSearchPath(target, dns.Type(dns.TypeSRV))
if err != nil {
return "", nil, err
}
for _, record := range response.Answer {
switch addr := record.(type) {
case *dns.SRV:
addrs = append(addrs, &net.SRV{
Weight: addr.Weight,View on GitHub (pinned to 35b8b99117)