kubernetes/kubernetes · error

usage of --node-cidr-mask-size-ipv4 and --node-cidr-mask-siz

Error message

usage of --node-cidr-mask-size-ipv4 and --node-cidr-mask-size-ipv6 is not allowed if --node-cidr-mask-size is set. For dual-stack clusters please unset it and use IPFamily specific flags

What it means

In single-stack mode the legacy --node-cidr-mask-size is authoritative and is mutually exclusive with the family-specific --node-cidr-mask-size-ipv4 / -ipv6 flags. KCM's setNodeCIDRMaskSizes rejects setting any family flag alongside the legacy one to avoid contradictory config.

Source

Thrown at cmd/kube-controller-manager/app/core.go:944

		if cfg.NodeCIDRMaskSizeIPv4 != 0 {
			ipv4Mask = int(cfg.NodeCIDRMaskSizeIPv4)
		}
		if cfg.NodeCIDRMaskSizeIPv6 != 0 {
			ipv6Mask = int(cfg.NodeCIDRMaskSizeIPv6)
		}
		return sortedSizes(ipv4Mask, ipv6Mask), nil
	}

	maskConfigured := cfg.NodeCIDRMaskSize != 0
	maskV4Configured := cfg.NodeCIDRMaskSizeIPv4 != 0
	maskV6Configured := cfg.NodeCIDRMaskSizeIPv6 != 0
	isSingleStackIPv6 := netutils.IsIPv6CIDR(clusterCIDRs[0])

	// original flag is set
	if maskConfigured {
		// original mask flag is still the main reference.
		if maskV4Configured || maskV6Configured {
			return nil, errors.New("usage of --node-cidr-mask-size-ipv4 and --node-cidr-mask-size-ipv6 is not allowed if --node-cidr-mask-size is set. For dual-stack clusters please unset it and use IPFamily specific flags")
		}

		mask := int(cfg.NodeCIDRMaskSize)
		return sortedSizes(mask, mask), nil
	}

	if maskV4Configured {
		if isSingleStackIPv6 {
			return nil, errors.New("usage of --node-cidr-mask-size-ipv4 is not allowed for a single-stack IPv6 cluster")
		}

		ipv4Mask = int(cfg.NodeCIDRMaskSizeIPv4)
	}

	// !maskV4Configured && !maskConfigured && maskV6Configured
	if maskV6Configured {
		if !isSingleStackIPv6 {
			return nil, errors.New("usage of --node-cidr-mask-size-ipv6 is not allowed for a single-stack IPv4 cluster")

View on GitHub (pinned to b882c60b40)

Solutions

  1. Keep only --node-cidr-mask-size (single-stack), or unset it and keep the family-specific flags.
  2. Lint the final flag set to ensure the legacy and family flags are not both present.

Example fix

# before
--node-cidr-mask-size=24 --node-cidr-mask-size-ipv6=64
# after
--node-cidr-mask-size=24
Defensive patterns

Strategy: validation

Validate before calling

maskConfigured := cfg.NodeCIDRMaskSize != 0
if maskConfigured && (cfg.NodeCIDRMaskSizeIPv4 != 0 || cfg.NodeCIDRMaskSizeIPv6 != 0) {
    return errors.New("--node-cidr-mask-size is mutually exclusive with v4/v6 mask flags")
}

Type guard

func mutuallyExclusiveMasksSet(cfg nodeipamconfig.NodeIPAMControllerConfiguration) bool {
    return cfg.NodeCIDRMaskSize != 0 && (cfg.NodeCIDRMaskSizeIPv4 != 0 || cfg.NodeCIDRMaskSizeIPv6 != 0)
}

Prevention

When it happens

Trigger: Single cluster CIDR, cfg.NodeCIDRMaskSize != 0 AND (NodeCIDRMaskSizeIPv4 != 0 OR NodeCIDRMaskSizeIPv6 != 0). core.go:941-944.

Common situations: Flag migration leaving both old and new set; chart defaults that populate all mask flags; merging configs from different sources.

Related errors


AI-assisted analysis of kubernetes/kubernetes@b882c60b40 (2026-08-07). Data as JSON: /api/errors/3dc767986c161017. Report an issue: GitHub.