cloudflare/cloudflared · error
expected at least 2 Cloudflare Regions regions, but SRV only
Error message
expected at least 2 Cloudflare Regions regions, but SRV only returned %v
What it means
ResolveEdge discovers Cloudflare edge addresses via SRV lookup and builds a Regions object that requires at least two addresses — one for each of the two logical edge regions (region1/region2) used for redundant connections. When EdgeDiscovery returns fewer than 2 addresses, cloudflared cannot construct redundant region connections, so this error is thrown reporting how many addresses the SRV lookup actually returned. The count is embedded in the message, e.g. 'SRV only returned 1'.
Source
Thrown at edgediscovery/allregions/regions.go:28
// Regions stores Cloudflare edge network IPs, partitioned into two regions.
// This is NOT thread-safe. Users of this package should use it with a lock.
type Regions struct {
region1 Region
region2 Region
}
// ------------------------------------
// Constructors
// ------------------------------------
// ResolveEdge resolves the Cloudflare edge, returning all regions discovered.
func ResolveEdge(log *zerolog.Logger, region string, overrideIPVersion ConfigIPVersion) (*Regions, error) {
edgeAddrs, err := EdgeDiscovery(log, RegionalServiceName(region))
if err != nil {
return nil, err
}
if len(edgeAddrs) < 2 {
return nil, fmt.Errorf("expected at least 2 Cloudflare Regions regions, but SRV only returned %v", len(edgeAddrs))
}
return &Regions{
region1: NewRegion(edgeAddrs[0], overrideIPVersion),
region2: NewRegion(edgeAddrs[1], overrideIPVersion),
}, nil
}
// StaticEdge creates a list of edge addresses from the list of hostnames.
// Mainly used for testing connectivity.
func StaticEdge(hostnames []string, log *zerolog.Logger) (*Regions, error) {
resolved := ResolveAddrs(hostnames, log)
if len(resolved) == 0 {
return nil, fmt.Errorf("failed to resolve any edge address")
}
return NewNoResolve(resolved), nil
}
// NewNoResolve doesn't resolve the edge. Instead it just uses the given addresses.View on GitHub (pinned to 2253eeeb25)
Solutions
- Retry — edge discovery is re-run on each connection attempt, and partial DNS answers are usually transient.
- Check the resolved count in the message: if it says 1, run 'nslookup -type=SRV <region service name>' yourself to confirm what your resolver returns and fix the resolver/VPN.
- Remove the --region restriction if set, so discovery can find addresses across all regions.
- Pin edge addresses explicitly with --edge <ip:port> list to bypass SRV-based region discovery entirely.
Example fix
// before // cloudflared tunnel --region us run <tunnel> // SRV only returned 1 // after — drop the region pin or pin the edge manually: // cloudflared tunnel --edge 198.41.192.7:7844,198.41.200.13:7844 run <tunnel>
Defensive patterns
Strategy: fallback
Validate before calling
func canDiscoverEnoughEdgeAddrs(service string) bool {
_, addrs, err := net.LookupSRV("", "", service)
if err != nil {
return false
}
n := 0
for _, srv := range addrs {
if ips, err := net.LookupIP(srv.Target); err == nil {
n += len(ips)
}
}
return n >= 2
} Try / catch
regions, err := allregions.ResolveEdge(log, region, ipVersion)
if err != nil {
if strings.Contains(err.Error(), "SRV only returned") {
logger.Warn().Err(err).Msg("insufficient edge addresses; falling back to explicit --edge list")
return useStaticEdgeAddrs() // fallback path
}
return err
} Prevention
- Avoid narrowing discovery with --region unless required; fewer regions means fewer addresses
- Verify corporate DNS/VPN returns complete SRV answers for Cloudflare edge domains before rollout
- Ensure both A and AAAA resolution works if you rely on a single IP version, or set --edge-ip-version to the family your network supports
- Keep a documented --edge fallback address list for degraded-DNS environments
When it happens
Trigger: Calling ResolveEdge (used when establishing tunnel connections) where EdgeDiscovery(RegionalServiceName(region)) succeeds but returns fewer than 2 EdgeAddr values — e.g. only one SRV target resolved, or repeated SRV targets collapsed to one address, or partial DNS failures dropped addresses.
Common situations: Degraded corporate DNS/VPN returning partial answers for Cloudflare SRV records; a region override (--region us) reducing discoverable addresses; network middleboxes stripping AAAA (or A) records so most targets fail resolution; transient DNS outages returning only one usable edge address.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- SRV record %v had no IPs
- failed to resolve any edge address
- no TXT record found for %s to determine which features to op
- unable to resolve host to confirm access
- failed to fetch JWKS from %s
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/1313b10bb73857af.
Report an issue: GitHub.