{"record":{"id":"555298589095ce13","repo":"kubernetes/kops","slug":"cannot-parse-rule-q","errorCode":null,"errorMessage":"cannot parse rule %q","messagePattern":"cannot parse rule %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/awstasks/securitygroup.go","lineNumber":412,"sourceCode":"\tMatches(permission *ec2types.SecurityGroupRule) bool\n}\n\n// ParseRemovalRule parses our removal rule DSL into a RemovalRule\nfunc ParseRemovalRule(rule string) (RemovalRule, error) {\n\trule = strings.TrimSpace(rule)\n\ttokens := strings.Split(rule, \"=\")\n\n\t// Simple little language:\n\t//   port=N matches rules that filter (only) by port=N\n\t//\n\t// Note this language is internal, so isn't required to be stable\n\n\tif len(tokens) == 2 {\n\t\tif tokens[0] == \"port\" {\n\t\t\tports := strings.SplitN(tokens[1], \":\", 2)\n\t\t\tfromPort, err := strconv.Atoi(ports[0])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot parse rule %q\", rule)\n\t\t\t}\n\t\t\ttoPort := fromPort\n\t\t\tif len(ports) > 1 {\n\t\t\t\ttoPort, err = strconv.Atoi(ports[1])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"cannot parse rule %q\", rule)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn &PortRemovalRule{\n\t\t\t\tFromPort: fromPort,\n\t\t\t\tToPort:   toPort,\n\t\t\t}, nil\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"cannot parse rule %q\", rule)\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"cannot parse rule %q\", rule)","sourceCodeStart":394,"sourceCodeEnd":430,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/awstasks/securitygroup.go#L394-L430","documentation":"ParseRemovalRule emits this when the from-port in a `port=N` / `port=N:M` removal rule cannot be converted to an integer with strconv.Atoi. It is the first of the port-parse failure paths. The error deliberately discards the strconv detail and just echoes the rule string.","triggerScenarios":"removeExtraRules entry like `port=abc`, `port=ssh`, `port=` (empty after '='), or `port=22-23` where tokens[1] split on ':' yields \"22-23\" which is not an integer.","commonSituations":"Using '-' instead of ':' as the range separator in port=22-23 (Atoi fails on \"22-23\" as from-port); specifying a service name instead of a number; accidental non-ASCII/whitespace characters in the spec value.","solutions":["Rewrite the rule with numeric ports: `port=22` or a range `port=22:23` (colon, not hyphen)","Confirm no stray characters (quotes, spaces, tabs) inside the value in the cluster spec","Run `kops toolbox`/dry-run (`kops update cluster` without --yes) to validate the spec before applying"],"exampleFix":"// before\nremoveExtraRules: [\"port=22-25\"]\n// after\nremoveExtraRules: [\"port=22:25\"]","handlingStrategy":"validation","validationCode":"func validPortRule(s string) bool {\n  parts := strings.SplitN(s, \"=\", 2)\n  if len(parts) != 2 || parts[0] != \"port\" { return false }\n  ports := strings.SplitN(parts[1], \":\", 2)\n  if _, err := strconv.Atoi(ports[0]); err != nil { return false }\n  if len(ports) > 1 {\n    if _, err := strconv.Atoi(ports[1]); err != nil { return false }\n  }\n  return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use ':' (not '-') for port ranges: port=22:443","Use numeric ports, never service names","Dry-run specs before applying"],"tags":["config","rule-parsing","ports","validation"],"backgroundTag":"invalid-rule-syntax","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}