kubernetes/kops · error

VPC ID is required when EgressOnlyInternetGateway is shared

Error message

VPC ID is required when EgressOnlyInternetGateway is shared

What it means

kOps' EgressOnlyInternetGateway task's Find() performs discovery of an existing Egress-Only Internet Gateway in AWS EC2. When the task is marked as Shared (i.e. kOps adopts an existing gateway instead of creating one), Find() must filter the DescribeEgressOnlyInternetGateways call by VPC ID, so a missing VPC reference is a hard precondition. If e.VPC.ID is empty while Shared is true, the function returns this error before making any AWS API call.

Source

Thrown at upup/pkg/fi/cloudup/awstasks/egressonlyinternetgateway.go:77

	}

	if len(response.EgressOnlyInternetGateways) != 1 {
		return nil, fmt.Errorf("found multiple EgressOnlyInternetGateways matching tags")
	}
	igw := response.EgressOnlyInternetGateways[0]
	return &igw, nil
}

func (e *EgressOnlyInternetGateway) Find(c *fi.CloudupContext) (*EgressOnlyInternetGateway, error) {
	ctx := c.Context()
	cloud := awsup.GetCloud(c)

	request := &ec2.DescribeEgressOnlyInternetGatewaysInput{}

	shared := fi.ValueOf(e.Shared)
	if shared {
		if fi.ValueOf(e.VPC.ID) == "" {
			return nil, fmt.Errorf("VPC ID is required when EgressOnlyInternetGateway is shared")
		}

		request.Filters = []ec2types.Filter{awsup.NewEC2Filter("attachment.vpc-id", *e.VPC.ID)}
	} else {
		if e.ID != nil {
			request.EgressOnlyInternetGatewayIds = []string{fi.ValueOf(e.ID)}
		} else {
			request.Filters = cloud.BuildFilters(e.Name)
		}
	}

	eigw, err := findEgressOnlyInternetGateway(ctx, cloud, request)
	if err != nil {
		return nil, err
	}
	if eigw == nil {
		return nil, nil
	}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure the EgressOnlyInternetGateway task has a valid VPC reference set (e.VPC.ID must be a non-empty VPC ID) before applying.
  2. Fix the cluster/spec so the VPC is defined (kops edit cluster / verify vpc field) and re-run kops update.
  3. If the gateway is NOT meant to be shared, unset Shared so Find() uses the ID-based lookup path instead of the VPC filter.

Example fix

// before
shared := fi.ValueOf(e.Shared)
if fi.ValueOf(e.VPC.ID) == "" { return nil, fmt.Errorf("VPC ID is required when EgressOnlyInternetGateway is shared") }
// after
// caller must ensure the task is built with a real VPC, e.g.:
// &awstasks.EgressOnlyInternetGateway{ Shared: fi.PtrTo(true), VPC: &awstasks.VPC{ID: fi.PtrTo("vpc-0abc123456789def0")} }
// set VPC.ID in the task construction so Find() can apply the attachment.vpc-id filter
Defensive patterns

Strategy: validation

Validate before calling

if fi.ValueOf(task.Shared) && (task.VPC == nil || fi.ValueOf(task.VPC.ID) == "") {
    return fmt.Errorf("EgressOnlyInternetGateway: Shared=true requires a non-empty VPC.ID")
}

Type guard

func hasVPCID(v *awstasks.VPC) bool {
    return v != nil && fi.ValueOf(v.ID) != ""
}

Prevention

When it happens

Trigger: Calling Find() (directly or via the fitask machinery during kops update/apply) on an EgressOnlyInternetGateway task whose Shared field is true and whose VPC.ID pointer is nil or set to the empty string.

Common situations: A cluster spec where the VPC task reference is not wired up (e.g. hand-edited manifests, partial shared-VPC configuration, or lifecycle overrides that drop the VPC link); using kops against an existing VPC with shared Egress-Only gateways for IPv6 egress and forgetting to specify the VPC.

Related errors


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