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
- Set spec.api.loadBalancer.type to exactly 'public' or 'internal' in the cluster spec
- Remove the empty loadBalancer block entirely if you want provider defaults
- Run 'kops get cluster -o yaml' to inspect the raw value for typos or casing issues
- 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
- On DigitalOcean always set type: public or type: internal explicitly
- Never copy loadBalancer blocks from AWS/GCP cluster specs
- Run a dry-run update before applying spec edits
- Keep the api.loadBalancer block empty-omitted unless you set a valid type
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
- unhandled LoadBalancer type %q
- failed to initialize digitalocean cloud: %s
- invalid base channel location: %q
- cannot find subnet %q (declared in instance group %q, not fo
- error parsing configuration: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/a9dd1b6681310a83.
Report an issue: GitHub.