kubernetes/kops · error
%T is not a *compute.BackendService
Error message
%T is not a *compute.BackendService
What it means
A defensive type assertion in listHealthchecks: a Resource's Obj field was expected to be a *compute.BackendService but held a different type. This indicates an internal inconsistency — listBackendServices returned resources whose payloads are not regional backend services — rather than a user or API failure.
Source
Thrown at pkg/resources/gce/gce.go:1163
}
return bs, nil
}
func (d *clusterDiscoveryGCE) listHealthchecks() ([]*resources.Resource, error) {
c := d.gceCloud
// TODO: cache, for efficiency, if needed.
// Find relevant healthchecks by finding all the backend services relevant to this
// cluster, then selecting all the healthchecks they use.
backendServices, err := d.listBackendServices()
if err != nil {
return nil, err
}
hcs := make(map[string]struct{})
for _, bs := range backendServices {
bsObj, ok := bs.Obj.(*compute.BackendService)
if !ok {
return nil, fmt.Errorf("%T is not a *compute.BackendService", bs)
}
for _, hc := range bsObj.HealthChecks {
hcs[hc] = struct{}{}
}
}
var hcResources []*resources.Resource
for hc := range hcs {
hcResources = append(hcResources, &resources.Resource{
Name: gce.LastComponent(hc),
ID: gce.LastComponent(hc),
Type: typeHealthcheck,
Deleter: func(cloud fi.Cloud, r *resources.Resource) error {
op, err := c.Compute().RegionHealthChecks().Delete(c.Project(), c.Region(), gce.LastComponent(hc))
if err != nil {
if gce.IsNotFound(err) {
klog.Infof("Healthcheck not found, assuming deleted: %q", r.Name)
return nil
}View on GitHub (pinned to 4c8573c808)
Solutions
- Confirm listBackendServices only inserts results from RegionBackendServices().List.
- Check the vendored gce cloud provider library version matches what kops expects (type may differ across versions).
- Extend the guard to log which resource produced the wrong type to speed diagnosis.
- This is an internal invariant violation — file/inspect kops code rather than changing cloud config.
Example fix
// before
bsObj, ok := bs.Obj.(*compute.BackendService)
if !ok {
return nil, fmt.Errorf("%T is not a *compute.BackendService", bs)
}
// after
bsObj, ok := bs.Obj.(*compute.BackendService)
if !ok {
return nil, fmt.Errorf("resource %q: %T is not a *compute.BackendService", bs.ID, bs.Obj)
} Defensive patterns
Strategy: type-guard
Validate before calling
// verify the resource list source before consuming
if producedByRegionList(backendServices) == false {
return fmt.Errorf("backend services list was not built from RegionBackendServices")
} Type guard
func asBackendService(r *resources.Resource) (*compute.BackendService, bool) {
bs, ok := r.Obj.(*compute.BackendService)
return bs, ok
} Try / catch
bsObj, ok := bs.Obj.(*compute.BackendService)
if !ok {
klog.Warningf("skipping resource %q: %T is not a *compute.BackendService", bs.ID, bs.Obj)
continue
} Prevention
- Keep the gce cloud library version in sync with what kops pins.
- Only feed listBackendServices output into listHealthchecks.
- Add a log of the concrete type when the assertion fails to spot drift early.
When it happens
Trigger: bs.Obj.(*compute.BackendService) fails because the resource tracker was populated with a different compute object type (e.g. a global backend service or a changed API type) — essentially only reachable if the resource list construction in listBackendServices drifts from this consumer's assumption.
Common situations: Mixing global vs regional backend service resources; a refactor or vendored cloud-library version change altering the concrete type stored in Resource.Obj; custom code reusing these list functions with other resource kinds.
Related errors
- failed to list backend services: %w
- unexpected target type for deletion: %T
- unexpected target type for deletion: %T
- error building node identifier: %w
- creating gce IPAM controller: %w
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/d81876196e45190f.
Report an issue: GitHub.