juanfont/headscale · critical · ErrNoPrefixConfigured
no IPv4 or IPv6 prefix configured, minimum one prefix is req
Error message
no IPv4 or IPv6 prefix configured, minimum one prefix is required
What it means
ErrNoPrefixConfigured is returned from hscontrol/types/config.go:1134 during prefix resolution when the ip_prefixes list yields no usable prefix. Every node in a tailnet must get an IP from the CGNAT range (default 100.64.0.0/10 and fd7a:115c:a1e0::/48), so with zero prefixes the server cannot allocate addresses and refuses to start. This is a fatal config-validation error, not a runtime one.
Source
Thrown at hscontrol/types/config.go:43
)
const (
PKCEMethodPlain string = "plain"
PKCEMethodS256 string = "S256"
defaultNodeStoreBatchSize = 100
)
var (
errOidcMutuallyExclusive = errors.New("oidc_client_secret and oidc_client_secret_path are mutually exclusive")
errOIDCIssuerInvalid = errors.New("oidc.issuer must be a valid http(s) URL")
errOIDCClientIDRequired = errors.New("oidc.client_id is required when oidc.issuer is set")
errOIDCClientSecretRequired = errors.New("oidc.client_secret or oidc.client_secret_path is required when oidc.issuer is set")
errServerURLSuffix = errors.New("server_url cannot be part of base_domain in a way that could make the DERP and headscale server unreachable")
errServerURLSame = errors.New("server_url cannot use the same domain as base_domain in a way that could make the DERP and headscale server unreachable")
errInvalidPKCEMethod = errors.New("pkce.method must be either 'plain' or 'S256'")
errTrustedProxyZeroRange = errors.New("0.0.0.0/0 and ::/0 are not allowed")
ErrNoPrefixConfigured = errors.New("no IPv4 or IPv6 prefix configured, minimum one prefix is required")
ErrInvalidAllocationStrategy = errors.New("invalid prefix allocation strategy")
)
type IPAllocationStrategy string
const (
IPAllocationStrategySequential IPAllocationStrategy = "sequential"
IPAllocationStrategyRandom IPAllocationStrategy = "random"
)
type PolicyMode string
const (
PolicyModeDB = "database"
PolicyModeFile = "file"
)
// EphemeralConfig contains configuration for ephemeral node lifecycle.View on GitHub (pinned to 565fd254d0)
Solutions
- Restore at least one prefix in config.yaml: ip_prefixes: ["100.64.0.0/10"] (IPv4 CGNAT) and/or ["fd7a:115c:a1e0::/48"] (IPv6 ULA)
- If you intentionally want no IPv4, keep the IPv6 prefix fd7a:115c:a1e0::/48 so allocation still works
- Validate with `headscale config verify` before restarting the service
Example fix
# before ip_prefixes: [] # after ip_prefixes: - fd7a:115c:a1e0::/48 - 100.64.0.0/10
Defensive patterns
Strategy: validation
Validate before calling
// ensure at least one prefix is configured before starting
if len(cfg.Prefixes.V4) == 0 && len(cfg.Prefixes.V6) == 0 {
return errors.New("ip_prefixes must contain at least one of 100.64.0.0/10 or fd7a:115c:a1e0::/48")
} Prevention
- Keep the default ip_prefixes block in config.yaml unless you deliberately replace it
- When narrowing prefixes to dodge CGNAT conflicts, always leave one family configured
- Validate config with `headscale config verify` after edits
When it happens
Trigger: Setting ip_prefixes: [] (empty list) in config.yaml; listing only prefixes that are filtered out (e.g. invalid families or unusable ranges) so the surviving list is empty; then calling LoadConfig / starting `headscale serve`.
Common situations: Commenting out the ip_prefixes block while experimenting and accidentally leaving an empty key; narrowing prefixes to avoid a CGNAT clash (per the disable-ipv4 nodeAttr docs) and removing both families; config migration between headscale versions where the key name or defaults changed.
Related errors
- Fatal config error: %s
- errOIDCIssuerInvalid
- ErrInvalidAllocationStrategy
- STUN address not set
- initial DERPMap is empty, Headscale requires at least one en
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/0d07a181533e7f6e.
Report an issue: GitHub.