{"record":{"id":"b000e47c4eb49e89","repo":"kubernetes/kops","slug":"cannot-specify-both-q-and-q-for-forwarding-rule","errorCode":null,"errorMessage":"cannot specify both %q and %q for forwarding rule target.","messagePattern":"cannot specify both %q and %q for forwarding rule target\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/gcetasks/forwardingrule.go","lineNumber":180,"sourceCode":"\t}\n\tif e.PortRange != nil {\n\t\to.PortRange = *e.PortRange\n\t}\n\tif len(e.Ports) > 0 {\n\t\to.Ports = e.Ports\n\t}\n\n\tif e.LoadBalancingScheme != nil {\n\t\to.LoadBalancingScheme = *e.LoadBalancingScheme\n\t}\n\n\tif e.TargetPool != nil {\n\t\to.Target = e.TargetPool.URL(t.Cloud)\n\t}\n\n\tif e.BackendService != nil {\n\t\tif o.Target != \"\" {\n\t\t\treturn fmt.Errorf(\"cannot specify both %q and %q for forwarding rule target.\", o.Target, e.BackendService)\n\t\t}\n\t\to.BackendService = e.BackendService.URL(t.Cloud)\n\t}\n\n\tif e.IPAddress != nil {\n\t\to.IPAddress = fi.ValueOf(e.IPAddress.IPAddress)\n\t\tif o.IPAddress == \"\" {\n\t\t\taddr, err := e.IPAddress.find(t.Cloud)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error finding Address %q: %v\", e.IPAddress, err)\n\t\t\t}\n\t\t\tif addr == nil {\n\t\t\t\treturn fmt.Errorf(\"Address %q was not found\", e.IPAddress)\n\t\t\t}\n\n\t\t\to.IPAddress = fi.ValueOf(addr.IPAddress)\n\t\t\tif o.IPAddress == \"\" {\n\t\t\t\treturn fmt.Errorf(\"Address had no IP: %v\", e.IPAddress)","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/gcetasks/forwardingrule.go#L162-L198","documentation":"RenderGCE builds the GCE ForwardingRule object and sets Target from either a TargetPool or a BackendService. GCE forwarding rules accept only one target, so if a TargetPool already produced a Target URL and a BackendService is also set, this validation error fires before any API call. It is a spec conflict, caught locally.","triggerScenarios":"A ForwardingRule task in the cluster spec has both targetPool and backendService populated; RenderGCE is called during kops update/apply.","commonSituations":"Migrating a cluster from target-pool-based load balancing to backend-service-based (or vice versa) while the old field was left populated; copy-paste of spec fragments; merge conflicts in the cluster manifest.","solutions":["Remove the targetPool field from the ForwardingRule spec if using a BackendService.","Alternatively remove backendService if the legacy TargetPool is intended.","Regenerate the manifest (kops edit cluster / kops update) to ensure only one target is set.","For migration, delete and recreate the forwarding rule with the new target type rather than specifying both."],"exampleFix":"// before\ntargetPool: api-target-pool\nbackendService: api-backend\n// after\nbackendService: api-backend","handlingStrategy":"validation","validationCode":"func validateForwardingRuleTarget(fr *ForwardingRuleSpec) error {\n  set := 0\n  if fr.TargetPool != nil { set++ }\n  if fr.BackendService != nil { set++ }\n  if set > 1 {\n    return errors.New(\"forwarding rule must specify exactly one of targetPool or backendService\")\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When migrating from TargetPool to BackendService load balancing, delete the old field, don't keep both.","Add a schema/manifest lint that enforces XOR on target fields.","Regenerate specs from kops templates instead of hand-merging fragments."],"tags":["gce","load-balancer","validation","conflict"],"backgroundTag":"conflicting-target-specified","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"}