kubernetes/kops · error

too many instances returned for the single instance-id

Error message

too many instances returned for the single instance-id

What it means

GetInstanceCertificateNames requires the single returned reservation to contain exactly one instance. If len(instances.Reservations[0].Instances) != 1, the DescribeInstances response does not match the assumed one-instance lookup, so certificate SANs cannot be derived safely.

Source

Thrown at pkg/bootstrap/awsbootstrap/verifier.go:498

	if err != nil {
		return nil, fmt.Errorf("building presigned request: %w", err)
	}
	u, err := url.Parse(signed.URL)
	if err != nil {
		return nil, fmt.Errorf("parsing presigned url: %w", err)
	}
	return &stsRequestValidator{Host: u.Host}, nil
}

// GetInstanceCertificateNames returns the instance names and addresses that should go into
// certificates: the instance ID, the private DNS name and the IP addresses.
func GetInstanceCertificateNames(instances *ec2.DescribeInstancesOutput) (addrs []string, err error) {
	if len(instances.Reservations) != 1 {
		return nil, fmt.Errorf("too many reservations returned for the single instance-id")
	}

	if len(instances.Reservations[0].Instances) != 1 {
		return nil, fmt.Errorf("too many instances returned for the single instance-id")
	}

	instance := instances.Reservations[0].Instances[0]

	addrs = append(addrs, *instance.InstanceId)

	if instance.PrivateDnsName != nil {
		addrs = append(addrs, *instance.PrivateDnsName)
	}

	// We only use data for the first interface, and only the first IP
	for _, iface := range instance.NetworkInterfaces {
		if iface.Attachment == nil {
			continue
		}
		if *iface.Attachment.DeviceIndex != 0 {
			continue
		}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Always filter DescribeInstances by the specific InstanceId from the caller identity
  2. If zero instances, treat as 'instance not found/terminated' and retry the bootstrap after confirming the node is running
  3. Log instance IDs in the reservation to identify which instances leaked into the result
  4. Retry after a short delay — EC2 describe results can lag instance state changes

Example fix

// before
out, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{Filters: filters})
// after
out, err := ec2Client.DescribeInstances(ctx, &ec2.DescribeInstancesInput{
  InstanceIds: []string{instanceID}, // exactly one instance expected
})
Defensive patterns

Strategy: validation

Validate before calling

if len(out.Reservations) != 1 || len(out.Reservations[0].Instances) != 1 {
  return fmt.Errorf("expected exactly 1 instance in reservation, got %d", len(out.Reservations[0].Instances))
}

Type guard

func isSingleInstance(out *ec2.DescribeInstancesOutput) bool {
  return out != nil && len(out.Reservations) == 1 && len(out.Reservations[0].Instances) == 1
}

Try / catch

addrs, err := GetInstanceCertificateNames(out)
if err != nil {
  return fmt.Errorf("cannot derive cert names: %w", err)
}

Prevention

When it happens

Trigger: verifyCallerIdentity passes a DescribeInstancesOutput where Reservations[0].Instances has zero entries (reservation placeholder with no instances, e.g. terminated instance) or multiple instances (query not filtered to a single instance-id).

Common situations: Instance was terminated between the STS call and DescribeInstances; missing InstanceIds filter returning sibling instances; spot interruption terminating the instance mid-bootstrap; eventual consistency in EC2 right after instance launch.

Related errors


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