{"record":{"id":"534c1570d9594e76","repo":"kubernetes/kops","slug":"found-multiple-elastic-ips-attached-to-natgateway","errorCode":null,"errorMessage":"found multiple elastic IPs attached to NatGateway %q","messagePattern":"found multiple elastic IPs attached to NatGateway %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"upup/pkg/fi/cloudup/awstasks/natgateway.go","lineNumber":111,"sourceCode":"\t\tngw, err = e.findNatGateway(c)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif ngw == nil {\n\t\t\treturn nil, nil\n\t\t}\n\t}\n\n\tactual.ID = ngw.NatGatewayId\n\n\tactual.Subnet = e.Subnet\n\tif len(ngw.NatGatewayAddresses) == 0 {\n\t\t// Not sure if this ever happens\n\t\tactual.ElasticIP = nil\n\t} else if len(ngw.NatGatewayAddresses) == 1 {\n\t\tactual.ElasticIP = &ElasticIP{ID: ngw.NatGatewayAddresses[0].AllocationId}\n\t} else {\n\t\treturn nil, fmt.Errorf(\"found multiple elastic IPs attached to NatGateway %q\", aws.ToString(ngw.NatGatewayId))\n\t}\n\n\t// NATGateways now have names and tags so lets pull from there instead.\n\tactual.Name = findNameTag(ngw.Tags)\n\tif e.Tags[\"Name\"] == \"\" {\n\t\t// If we're not tagging by name, avoid spurious differences\n\t\tactual.Name = e.Name\n\t}\n\tactual.Tags = intersectTags(ngw.Tags, e.Tags)\n\n\t// Avoid spurious changes\n\tactual.Lifecycle = e.Lifecycle\n\tactual.Shared = e.Shared\n\tactual.AssociatedRouteTable = e.AssociatedRouteTable\n\n\te.ID = actual.ID\n\treturn actual, nil\n}","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/awstasks/natgateway.go#L93-L129","documentation":"Same single-EIP invariant as [2027] but raised in the later address-mapping block of Find(): when ngw.NatGatewayAddresses contains more than one entry, kOps refuses to guess which AllocationId corresponds to actual.ElasticIP and returns this error naming the gateway.","triggerScenarios":"DescribeNatGateways returned a gateway with >=2 NatGatewayAddresses while Find() maps addresses to the ElasticIP field — same external reassociation/mutation causes as [2027].","commonSituations":"Manual EIP juggling, third-party cost/IP management tools reattaching addresses, partial cleanup of a failed manual migration.","solutions":["Disassociate the surplus EIP from the NAT gateway","Verify with aws ec2 describe-nat-gateways that only one address remains","Re-run kops update cluster to converge state","Prevent external tooling from modifying NAT gateway addresses"],"exampleFix":"// before\nlen(ngw.NatGatewayAddresses) == 2 // eipassoc-abc + eipassoc-def\n// after\naws ec2 disassociate-address --association-id eipassoc-def // back to one address","handlingStrategy":"validation","validationCode":"addrs := ngw.NatGatewayAddresses\nif len(addrs) > 1 {\n  for _, a := range addrs[1:] {\n    // disassociate a.AssociationId before reconcile\n  }\n}","typeGuard":null,"tryCatchPattern":"if len(ngw.NatGatewayAddresses) > 1 {\n  return fmt.Errorf(\"fix NAT gateway %s manually (disassociate extra EIPs) before re-running kops\", aws.ToString(ngw.NatGatewayId))\n}","preventionTips":["Keep exactly one EIP per NAT gateway","After manual EIP work, always re-run kops update cluster to verify","Document NAT gateway EIPs as kOps-managed resources"],"tags":["aws","ec2","nat-gateway","elastic-ip"],"backgroundTag":"multiple-elastic-ips","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"}