{"record":{"id":"36ed4157344f44db","repo":"kubernetes/kops","slug":"unable-to-calculate-cidr-mask-size-for-q-q","errorCode":null,"errorMessage":"unable to calculate CIDR mask size for %q: %q","messagePattern":"unable to calculate CIDR mask size for %q: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/utils/net.go","lineNumber":112,"sourceCode":"\tif err != nil {\n\t\treturn 0, 0, fmt.Errorf(\"unable to convert CIDR subnet net num to int: %q: %v\", s[2], err)\n\t}\n\n\treturn newSize, netNum, nil\n}\n\n// CIDRSubnet calculates a subnet address within given IP network address prefix.\n// Inspired by the Terraform implementation of the \"cidrsubnet\" function\n// https://www.terraform.io/docs/language/functions/cidrsubnet.html\nfunc CIDRSubnet(prefix string, newSize int, netNum int64) (string, error) {\n\t_, baseCIDR, err := net.ParseCIDR(prefix)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to parse CIDR for %q: %v\", prefix, err)\n\t}\n\n\toldSize, totalSize := baseCIDR.Mask.Size()\n\tif oldSize == 0 && totalSize == 0 {\n\t\treturn \"\", fmt.Errorf(\"unable to calculate CIDR mask size for %q: %q\", prefix, baseCIDR.Mask)\n\t}\n\n\tnewNetwork, err := cidr.SubnetBig(baseCIDR, newSize-oldSize, big.NewInt(netNum))\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to calculate subnet CIDR for %q -> /%d#%d : %v\", prefix, newSize, netNum, err)\n\t}\n\n\treturn newNetwork.String(), nil\n}\n","sourceCodeStart":94,"sourceCodeEnd":122,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/utils/net.go#L94-L122","documentation":"After successfully parsing the prefix, CIDRSubnet calls baseCIDR.Mask.Size() to obtain the existing mask size and total address bits. Both are 0 only when the mask size cannot be determined (effectively an unusable mask), so the function refuses to proceed with this error.","triggerScenarios":"Calling CIDRSubnet with a prefix that parses but whose mask has no determinable size — practically rare, but reachable when the parsed CIDR carries a degenerate/invalid mask (e.g. through custom parsing paths or corrupted input that still satisfies net.ParseCIDR).","commonSituations":"Corrupted or programmatically mangled CIDR strings in cluster config; intermediate tooling rewriting networkCIDR into a form Go's parser accepts but whose mask is meaningless; fuzzed or adversarial input to the validation path.","solutions":["Replace the prefix with a canonical, well-formed CIDR (e.g. \"10.0.0.0/16\") and retry.","Re-derive the CIDR from the cluster spec rather than from the corrupted value.","Log the offending prefix and mask for debugging, then fix the upstream source of the value.","Add a pre-call sanity check that a parsed CIDR's Mask.Size() returns a non-zero oldSize."],"exampleFix":"// before\nsubnet, err := utils.CIDRSubnet(corruptedPrefix, 8, 1) // corrupted/undeterminable mask\n// after\nsubnet, err := utils.CIDRSubnet(\"172.20.0.0/16\", 8, 1)","handlingStrategy":"validation","validationCode":"func hasSizableMask(prefix string) bool {\n\t_, cidrNet, err := net.ParseCIDR(prefix)\n\tif err != nil {\n\t\treturn false\n\t}\n\told, total := cidrNet.Mask.Size()\n\treturn old != 0 || total != 0\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use canonical CIDR strings from known-good sources (kops-generated specs) rather than hand-mangled values.","Sanity-check Mask.Size() output when ingesting CIDRs from untrusted tooling.","Log the prefix and parsed mask when validation fails to speed diagnosis."],"tags":["network","cidr","subnet","mask"],"backgroundTag":"invalid-cidr-mask","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}