kubernetes/kops · error

unhandled LoadBalancer type %q

Error message

unhandled LoadBalancer type %q

What it means

The DigitalOcean API load balancer model (pkg/model/domodel/api_loadbalancer.go:55) only supports LB type Internal and Public. If spec.api.loadBalancer.type in the cluster spec is any other value (empty string included, since the switch has no default case for it), Build returns 'unhandled LoadBalancer type'. It is a strict enum check against kops.LoadBalancerType.

Source

Thrown at pkg/model/domodel/api_loadbalancer.go:55

func (b *APILoadBalancerModelBuilder) Build(c *fi.CloudupModelBuilderContext) error {
	// Configuration where a load balancer fronts the API
	if !b.UseLoadBalancerForAPI() {
		return nil
	}

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

	switch lbSpec.Type {
	case kops.LoadBalancerTypeInternal:
		// OK
	case kops.LoadBalancerTypePublic:
		// OK
	default:
		return fmt.Errorf("unhandled LoadBalancer type %q", lbSpec.Type)
	}

	clusterName := do.SafeClusterName(b.ClusterName())
	loadbalancerName := "api-" + clusterName
	clusterMasterTag := do.TagKubernetesClusterMasterPrefix + ":" + clusterName

	// Create LoadBalancer for API LB
	loadbalancer := &dotasks.LoadBalancer{
		Name:              new(loadbalancerName),
		Region:            new(b.Cluster.Spec.Networking.Subnets[0].Region),
		DropletTag:        new(clusterMasterTag),
		Lifecycle:         b.Lifecycle,
		WellKnownServices: []wellknownservices.WellKnownService{wellknownservices.KopsController, wellknownservices.KubeAPIServer},
	}

	if b.Cluster.Spec.Networking.NetworkID != "" {
		loadbalancer.VPCUUID = new(b.Cluster.Spec.Networking.NetworkID)
	} else if b.Cluster.Spec.Networking.NetworkCIDR != "" {

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Set spec.api.loadBalancer.type to exactly 'public' or 'internal' in the cluster spec
  2. Remove the empty loadBalancer block entirely if you want provider defaults
  3. Run 'kops get cluster -o yaml' to inspect the raw value for typos or casing issues
  4. Re-run 'kops update cluster' after the fix

Example fix

// before
api:
  loadBalancer: {}
// after
api:
  loadBalancer:
    type: public
Defensive patterns

Strategy: validation

Validate before calling

lb := cluster.Spec.API.LoadBalancer
if lb != nil && lb.Type != "" && lb.Type != kops.LoadBalancerTypePublic && lb.Type != kops.LoadBalancerTypeInternal {
	return fmt.Errorf("DO clusters only support loadBalancer type public|internal, got %q", lb.Type)
}
if lb != nil && lb.Type == "" { return errors.New("api.loadBalancer.type must be set (public|internal)") }

Type guard

func supportedDOLBType(t kops.LoadBalancerType) bool {
	return t == kops.LoadBalancerTypePublic || t == kops.LoadBalancerTypeInternal
}

Prevention

When it happens

Trigger: Setting spec.api.loadBalancer.type to a value outside {internal, public} — e.g. an empty string because the loadBalancer block was created but the type field omitted, or a copy-pasted AWS-style value.

Common situations: Templated cluster manifests where the type field was left blank; hand-editing the spec and using a provider-unsupported type name (e.g. 'External'); schema changes across kOps versions altering the accepted enum.

Related errors


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