kubernetes/kops · error

unhandled LoadBalancer type %q

Error message

unhandled LoadBalancer type %q

What it means

APILoadBalancerBuilder.Build in pkg/model/awsmodel builds the AWS load balancer task for the kOps API endpoint. The cluster spec's api.loadBalancer.type is only allowed to be "Internal" or "Public"; any other string makes the builder fail fast with this error rather than silently provisioning the wrong LB type.

Source

Thrown at pkg/model/awsmodel/api_loadbalancer.go:61

// Build is responsible for building the KubeAPI tasks for the aws model
func (b *APILoadBalancerBuilder) Build(c *fi.CloudupModelBuilderContext) error {
	// Configuration where an ELB fronts the API
	if !b.UseLoadBalancerForAPI() {
		return nil
	}

	lbSpec := b.Cluster.Spec.API.LoadBalancer
	if lbSpec == nil {
		// Skipping API ELB creation; not requested in Spec
		return nil
	}

	switch lbSpec.Type {
	case kops.LoadBalancerTypeInternal, kops.LoadBalancerTypePublic:
	// OK

	default:
		return fmt.Errorf("unhandled LoadBalancer type %q", lbSpec.Type)
	}

	var nlbSubnetMappings []*awstasks.SubnetMapping
	if len(lbSpec.Subnets) != 0 {
		// Subnets have been explicitly set
		for _, subnet := range lbSpec.Subnets {
			for _, clusterSubnet := range b.Cluster.Spec.Networking.Subnets {
				if subnet.Name == clusterSubnet.Name {
					nlbSubnetMapping := &awstasks.SubnetMapping{
						Subnet: b.LinkToSubnet(&clusterSubnet),
					}
					if subnet.PrivateIPv4Address != nil {
						nlbSubnetMapping.PrivateIPv4Address = subnet.PrivateIPv4Address
					}
					if subnet.AllocationID != nil {
						nlbSubnetMapping.AllocationID = subnet.AllocationID
					}
					nlbSubnetMappings = append(nlbSubnetMappings, nlbSubnetMapping)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Set api.loadBalancer.type to exactly "Internal" or "Public" in the cluster spec
  2. Run `kops set cluster` / `kops edit cluster` instead of hand-editing to avoid typos
  3. Trim whitespace and match case exactly, then `kops update cluster` again

Example fix

# before
api:
  loadBalancer:
    type: private
# after
api:
  loadBalancer:
    type: Internal
Defensive patterns

Strategy: validation

Validate before calling

lb := cluster.Spec.API.LoadBalancer
if lb != nil && lb.Type != kops.LoadBalancerTypeInternal && lb.Type != kops.LoadBalancerTypePublic {
    return fmt.Errorf("api.loadBalancer.type must be Internal or Public, got %q", lb.Type)
}

Type guard

func validLBType(t string) bool {
    return t == kops.LoadBalancerTypeInternal || t == kops.LoadBalancerTypePublic
}

Try / catch

if err := kopsUpdateCluster(cluster); err != nil {
    if strings.Contains(err.Error(), "unhandled LoadBalancer type") {
        // fix spec.api.loadBalancer.type and retry
    }
    return err
}

Prevention

When it happens

Trigger: Running `kops update cluster` when cluster spec has api.loadBalancer.type set to a value other than Internal/Public (typo like "internal " (trailing space), "private", "external", or mixed case like "PUBLIC").

Common situations: Hand-edited cluster.yaml after upgrade, snippets copied from docs for other clouds, or specs written before validation for this field existed.

Related errors


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