docker/cli · error

invalid IP address

Error message

invalid IP address: %s

What it means

Raised while converting --default-addr-pool entries to netip.Prefix during 'docker swarm init'. After normalizing a pool's IP to 4 or 16 bytes, netip.AddrFromSlice rejects the slice (e.g. a zero-length IP), which means the supplied CIDR could not yield a valid address.

Solutions

  1. Use a well-formed CIDR like '10.0.0.0/8' or '192.168.0.0/16'.
  2. Verify each pool parses with 'ipcalc <cidr>' or 'python3 -c "import ipaddress; ipaddress.ip_network(''CIDR'')"'.
  3. Drop --default-addr-pool to accept Docker's default address pools.

Example fix

# before
docker swarm init --default-addr-pool /24

# after
docker swarm init --default-addr-pool 10.0.0.0/8 --subnet-size 24
Defensive patterns

Strategy: validation

Validate before calling

// Validate each CIDR before init
for _, cidr := range pools {
    if _, err := netip.ParsePrefix(cidr); err != nil {
        return fmt.Errorf("invalid default-addr-pool %q: %w", cidr, err)
    }
}

Type guard

func validCIDR(s string) bool {
	_, err := netip.ParsePrefix(s)
	return err == nil
}

Prevention

When it happens

Trigger: Passing --default-addr-pool with a malformed CIDR whose IP portion is empty or otherwise unparseable as IPv4/IPv6 (e.g. an all-zero mask with empty IP, or a degenerate IPNet). The loop at init.go:73-88 fails when netip.AddrFromSlice(ip) returns ok==false.

Common situations: Typo in CIDR; passing '/24' without an address; programmatically building net.IPNet values with nil IP; shell quoting stripping the IP portion.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/37e8b0e7faae52cd. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/swarm/init.go:84

	return cmd
}

func runInit(ctx context.Context, dockerCLI command.Cli, flags *pflag.FlagSet, opts initOptions) error {
	apiClient := dockerCLI.Client()

	// TODO(thaJeztah): change opts.defaultAddrPools a []netip.Prefix; see https://github.com/docker/cli/pull/6545#discussion_r2420361609
	defaultAddrPool := make([]netip.Prefix, 0, len(opts.defaultAddrPools))
	for _, p := range opts.defaultAddrPools {
		if len(p.IP) == 0 {
			continue
		}
		ip := p.IP.To4()
		if ip == nil {
			ip = p.IP.To16()
		}
		addr, ok := netip.AddrFromSlice(ip)
		if !ok {
			return fmt.Errorf("invalid IP address: %s", p.IP)
		}
		ones, _ := p.Mask.Size()
		defaultAddrPool = append(defaultAddrPool, netip.PrefixFrom(addr, ones))
	}
	var availability swarm.NodeAvailability
	if flags.Changed(flagAvailability) {
		switch a := swarm.NodeAvailability(strings.ToLower(opts.availability)); a {
		case swarm.NodeAvailabilityActive, swarm.NodeAvailabilityPause, swarm.NodeAvailabilityDrain:
			availability = a
		default:
			return fmt.Errorf("invalid availability %q, only active, pause and drain are supported", opts.availability)
		}
	}

	res, err := apiClient.SwarmInit(ctx, client.SwarmInitOptions{
		ListenAddr:       opts.listenAddr.String(),
		AdvertiseAddr:    opts.advertiseAddr,
		DataPathAddr:     opts.dataPathAddr,

View on GitHub (pinned to 4f84911bfe)