kubernetes/kops · error

NextToken set from DescribeNatGateways, but pagination not i

Error message

NextToken set from DescribeNatGateways, but pagination not implemented

What it means

kops' FindNatGateways calls DescribeNatGateways without pagination support; if AWS returns a NextToken (result set larger than one page), it deliberately fails fast rather than silently dropping NAT gateways. This is a defensive limitation error, not an AWS failure.

Source

Thrown at pkg/resources/aws/aws.go:1396

		}
	}

	var resourceTrackers []*resources.Resource
	for natGatewayId := range natGatewayIds {
		request := &ec2.DescribeNatGatewaysInput{
			NatGatewayIds: []string{natGatewayId},
		}
		response, err := c.EC2().DescribeNatGateways(ctx, request)
		if err != nil {
			if awsup.AWSErrorCode(err) == "NatGatewayNotFound" {
				klog.V(2).Infof("Got NatGatewayNotFound describing NatGateway %s; will treat as already-deleted", natGatewayId)
				continue
			}
			return nil, fmt.Errorf("error from DescribeNatGateways: %v", err)
		}

		if response.NextToken != nil {
			return nil, fmt.Errorf("NextToken set from DescribeNatGateways, but pagination not implemented")
		}

		for _, ngw := range response.NatGateways {
			natGatewayId := aws.ToString(ngw.NatGatewayId)

			forceShared := !ownedNatGatewayIds.Has(natGatewayId)
			ngwResource := buildNatGatewayResource(ngw, forceShared, clusterName)
			resourceTrackers = append(resourceTrackers, ngwResource)

			// Don't try to remove ElasticIPs if NatGateway is shared
			if ngwResource.Shared {
				continue
			}

			// If we're deleting the NatGateway, we should delete the ElasticIP also
			for _, address := range ngw.NatGatewayAddresses {
				if address.AllocationId != nil {
					request := &ec2.DescribeAddressesInput{}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Upgrade kops to a version where DescribeNatGateways pagination is implemented.
  2. Work around by reducing matching NAT gateways (filters on the cluster's VPC only) or running per-VPC.
  3. Patch locally: convert to ec2.NewDescribeNatGatewaysPaginator and iterate pages like DescribeLaunchTemplates does.

Example fix

// before
response, err := c.EC2().DescribeNatGateways(ctx, request)
if response.NextToken != nil {
	return nil, fmt.Errorf("NextToken set from DescribeNatGateways, but pagination not implemented")
}
// after
paginator := ec2.NewDescribeNatGatewaysPaginator(c.EC2(), request)
for paginator.HasMorePages() {
	page, err := paginator.NextPage(ctx)
	if err != nil {
		return nil, fmt.Errorf("error from DescribeNatGateways: %w", err)
	}
	// process page.NatGateways
}
Defensive patterns

Strategy: validation

Validate before calling

if len(natGatewayIDs) > 50 { /* page manually or split into batches — large sets can return NextToken */ }

Type guard

func nextTokenSet(resp *ec2.DescribeNatGatewaysOutput) bool { return resp != nil && resp.NextToken != nil }

Try / catch

if err != nil && strings.Contains(err.Error(), "pagination not implemented") {
	// fall back to per-ID DescribeNatGateways calls which never paginate
	return listNatGatewaysOneByOne(ids)
}

Prevention

When it happens

Trigger: DescribeNatGateways response contains a non-nil NextToken — i.e., more NAT gateways match the filter than fit in a single API page.

Common situations: Enumerating resources in an account/region with many NAT gateways (many large multi-cluster setups) sharing the same VPC filters.

Related errors


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