kubernetes/kops · error

found multiple elastic IPs attached to NatGateway

Error message

found multiple elastic IPs attached to NatGateway %q

What it means

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.

Solutions

  1. Disassociate the surplus EIP from the NAT gateway
  2. Verify with aws ec2 describe-nat-gateways that only one address remains
  3. Re-run kops update cluster to converge state
  4. Prevent external tooling from modifying NAT gateway addresses

Example fix

// before
len(ngw.NatGatewayAddresses) == 2 // eipassoc-abc + eipassoc-def
// after
aws ec2 disassociate-address --association-id eipassoc-def // back to one address
Defensive patterns

Strategy: validation

Validate before calling

addrs := ngw.NatGatewayAddresses
if len(addrs) > 1 {
  for _, a := range addrs[1:] {
    // disassociate a.AssociationId before reconcile
  }
}

Try / catch

if len(ngw.NatGatewayAddresses) > 1 {
  return fmt.Errorf("fix NAT gateway %s manually (disassociate extra EIPs) before re-running kops", aws.ToString(ngw.NatGatewayId))
}

Prevention

When it happens

Trigger: DescribeNatGateways returned a gateway with >=2 NatGatewayAddresses while Find() maps addresses to the ElasticIP field — same external reassociation/mutation causes as [2027].

Common situations: Manual EIP juggling, third-party cost/IP management tools reattaching addresses, partial cleanup of a failed manual migration.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/534c1570d9594e76. Report an issue: GitHub.

Appendix: source

Thrown at upup/pkg/fi/cloudup/awstasks/natgateway.go:111

		ngw, err = e.findNatGateway(c)
		if err != nil {
			return nil, err
		}
		if ngw == nil {
			return nil, nil
		}
	}

	actual.ID = ngw.NatGatewayId

	actual.Subnet = e.Subnet
	if len(ngw.NatGatewayAddresses) == 0 {
		// Not sure if this ever happens
		actual.ElasticIP = nil
	} else if len(ngw.NatGatewayAddresses) == 1 {
		actual.ElasticIP = &ElasticIP{ID: ngw.NatGatewayAddresses[0].AllocationId}
	} else {
		return nil, fmt.Errorf("found multiple elastic IPs attached to NatGateway %q", aws.ToString(ngw.NatGatewayId))
	}

	// NATGateways now have names and tags so lets pull from there instead.
	actual.Name = findNameTag(ngw.Tags)
	if e.Tags["Name"] == "" {
		// If we're not tagging by name, avoid spurious differences
		actual.Name = e.Name
	}
	actual.Tags = intersectTags(ngw.Tags, e.Tags)

	// Avoid spurious changes
	actual.Lifecycle = e.Lifecycle
	actual.Shared = e.Shared
	actual.AssociatedRouteTable = e.AssociatedRouteTable

	e.ID = actual.ID
	return actual, nil
}

View on GitHub (pinned to 4c8573c808)