kubernetes/kops · error
server-side addons client does not support Addons::Replace
Error message
server-side addons client does not support Addons::Replace
What it means
The controller-side addons clientset is a read-only, server-side view of addons rendered from local files. Replace (mutating an addons list) is intentionally unimplemented and always returns this sentinel error. It signals 'this operation is not supported by this client', not a transient failure.
Source
Thrown at cmd/kops-controller/pkg/controllerclientset/addons.go:53
}
var _ simple.AddonsClient = &addonsClient{}
func newAddonsClient(basePath vfs.Path, cluster *kops.Cluster) *addonsClient {
if cluster == nil || cluster.Name == "" {
klog.Fatalf("cluster / cluster.Name is required")
}
r := &addonsClient{
basePath: basePath,
cluster: cluster,
}
return r
}
func (c *addonsClient) Replace(addons kubemanifest.ObjectList) error {
return fmt.Errorf("server-side addons client does not support Addons::Replace")
}
func (c *addonsClient) List(ctx context.Context) (kubemanifest.ObjectList, error) {
configPath := c.basePath.Join("default")
b, err := configPath.ReadFile(ctx)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("error reading addons file %s: %v", configPath, err)
}
objects, err := kubemanifest.LoadObjectsFrom(b)
if err != nil {
return nil, err
}
View on GitHub (pinned to 4c8573c808)
Solutions
- Refactor the calling code to skip addon replacement when running controller-side, or use the full kops CLI clientset
- Guard the call site: only invoke Replace from tooling that uses the file-backed admin clientset
- If addon mutation is needed from the controller, implement Replace in addonsClient against the API server
Example fix
// before
if err := addonsClient.Replace(addons); err != nil {
return err
}
// after
if err := addonsClient.Replace(addons); err != nil {
if err.Error() == "server-side addons client does not support Addons::Replace" {
return nil // unsupported controller-side; managed via kops CLI
}
return err
} Defensive patterns
Strategy: type-guard
Validate before calling
// detect the unsupported client before mutating
type replacer interface{ Replace(kubemanifest.ObjectList) error }
if _, ok := addonsClient.(replacer); !ok {
// this clientset cannot Replace; route via kops CLI instead
} Type guard
func supportsReplace(c interface{}) bool {
r, ok := c.(interface{ Replace(kubemanifest.ObjectList) error })
_ = r
return ok
} Try / catch
if err := addonsClient.Replace(addons); err != nil {
if err.Error() == "server-side addons client does not support Addons::Replace" {
return nil // expected on controller-side clientset
}
return err
} Prevention
- Know that controllerclientset is read-only; use kops CLI for addon mutations
- Gate addons-apply code paths on clientset capability
- Document Replace as unsupported in shared code
When it happens
Trigger: Any code path calls addonsClient.Replace(addons) — e.g. RunCreate or CreateClusterConfig invoking the addons clientset from within kops-controller, where only List/Get-style reads are supported.
Common situations: Reusing kops bootstrup/create code (which does addons replace) inside the controller; a new feature tried to apply addon changes via the controller clientset instead of `kops replace`/the admin CLI.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- error parsing addons: %v
- building menu for %q: %w
- applying %q: %w
- failed to get updates: %w
- updating %q: %w
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/eb1ea8a8ef6cf4c2.
Report an issue: GitHub.