juanfont/headscale · error

allocating IPv4 address: %w

Error message

allocating IPv4 address: %w

What it means

IPAllocator.Next() tries to claim the next free IPv4 address from the configured prefix (sequentially or from a random start). This wrapper fires when allocateNext fails for the v4 family: the pool is exhausted, or the used-IP set could not be built/queried.

Source

Thrown at hscontrol/db/ip.go:150

		)
	}

	ret.usedIPs = ips

	return &ret, nil
}

func (i *IPAllocator) Next() (*netip.Addr, *netip.Addr, error) {
	var (
		err  error
		ret4 *netip.Addr
		ret6 *netip.Addr
	)

	if i.prefix4 != nil {
		ret4, err = i.allocateNext(&i.prev4, i.prefix4)
		if err != nil {
			return nil, nil, fmt.Errorf("allocating IPv4 address: %w", err)
		}
	}

	if i.prefix6 != nil {
		ret6, err = i.allocateNext(&i.prev6, i.prefix6)
		if err != nil {
			return nil, nil, fmt.Errorf("allocating IPv6 address: %w", err)
		}
	}

	return ret4, ret6, nil
}

var ErrCouldNotAllocateIP = errors.New("failed to allocate IP")

// allocateNext allocates the next address from prefix under i.mu, advancing
// prev so a run of allocations (e.g. BackfillNodeIPs) does not rescan
// already-issued addresses, and so prev is read under the lock rather than in

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Enlarge the IPv4 prefix in ip_prefixes (or remove the v4 prefix entirely to run v6-only).
  2. Delete decommissioned nodes to free addresses: headscale nodes delete -I <id>.
  3. Recount usage vs capacity: count rows in nodes against usable hosts in the prefix.

Example fix

# before (config.yaml)
ip_prefixes:
  - 100.100.0.0/28 # ~14 usable, exhausted

# after
ip_prefixes:
  - 100.64.0.0/10 # default, huge pool
  - fd7a:115c:a1e0::/48
Defensive patterns

Strategy: fallback

Validate before calling

// Capacity planning check before adding nodes:
// usable := prefix bits -> hosts; compare with node count.
func capacityOK(prefix netip.Prefix, nodeCount int) bool {
    bits := prefix.Bits()
    total := new(big.Int).Lsh(big.NewInt(1), uint(prefix.Addr().BitLen()-bits))
    usable := new(big.Int).Sub(total, big.NewInt(2)) // network + broadcast reserved
    return big.NewInt(int64(nodeCount)).Cmp(usable) < 0
}

Try / catch

// On allocation failure, fall back to enqueueing/retrying node registration
// after freeing addresses (delete stale nodes) or widening the prefix.
// Do not silently hand out addresses outside the prefix.

Prevention

When it happens

Trigger: More nodes (or reserved addresses) than fit in the ipv4 prefix — e.g. a /24 with ~250 usable addresses fully allocated; or the usedIPs IPSet() call inside allocateNext returning an error.

Common situations: Small custom ip_prefixes (like a /28) on a busy tailnet; default 100.64.0.0/10 is huge so exhaustion usually implies a custom narrow prefix; mass node registration via pre-auth keys or automation.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/9cb78e5cbb2a069c. Report an issue: GitHub.