kubernetes/kops · error
cannot unset field %q (marked immutable)
Error message
cannot unset field %q (marked immutable)
What it means
During Unset, when the visitor reaches the exact field, it checks reflect Value.CanSet(). If the field is not settable — e.g. obtained through an unexported field, a nil-pointer dereference path, or a value (non-pointer) copy — the field is effectively immutable and this error is returned rather than silently skipping the zeroing.
Source
Thrown at util/pkg/reflectutils/access.go:323
func Unset(target interface{}, targetPath string) error {
targetValue := reflect.ValueOf(target)
targetFieldPath, err := ParseFieldPath(targetPath)
if err != nil {
return fmt.Errorf("cannot parse field path %q: %w", targetPath, err)
}
fieldUnset := false
visitor := func(path *FieldPath, field *reflect.StructField, v reflect.Value) error {
if !targetFieldPath.HasPrefixMatch(path) {
return nil
}
if targetFieldPath.Matches(path) {
if !v.CanSet() {
return fmt.Errorf("cannot unset field %q (marked immutable)", path)
}
v.Set(reflect.Zero(v.Type()))
fieldUnset = true
return nil
}
return nil
}
err = ReflectRecursive(targetValue, visitor, &ReflectOptions{JSONNames: true})
if err != nil {
return err
}
if !fieldUnset {
return fmt.Errorf("field %s not found in %s", targetPath, BuildTypeName(reflect.TypeOf(target)))
}View on GitHub (pinned to 4c8573c808)
Solutions
- Ensure the target object is passed as a pointer (*api.Cluster / *api.InstanceGroup)
- Choose a different field path that traverses only exported, pointer-addressable fields
- Fall back to editing the YAML manifest directly and `kops replace -f`
Example fix
// before
var c api.Cluster
Unset(&wrapper, "spec.kubelet") // unreachable inner field
// after
c := api.Cluster{}
Unset(&c, "spec.kubelet.cpuCFSQuota") // pointer to top-level object Defensive patterns
Strategy: validation
Validate before calling
if reflect.ValueOf(target).Kind() != reflect.Ptr || reflect.ValueOf(target).IsNil() {
return fmt.Errorf("target must be a non-nil pointer")
} Type guard
func isSettablePointer(target interface{}) bool {
v := reflect.ValueOf(target)
return v.Kind() == reflect.Ptr && !v.IsNil() && v.Elem().CanSet()
} Try / catch
if err := Unset(target, path); err != nil && strings.Contains(err.Error(), "marked immutable") {
// edit the manifest and `kops replace -f` instead
} Prevention
- Always pass *api.Cluster / *api.InstanceGroup pointers to Unset
- Avoid paths traversing unexported or value-only fields
- For read-only fields, modify the YAML manifest directly
When it happens
Trigger: Calling kops unset on a field that the reflection walk cannot address for writing, e.g. unsetting a field inside a struct reached through an unexported field or an interface/value-typed portion of the spec.
Common situations: Attempting to unset fields inside embedded read-only structures or on objects passed by value; kops API types are normally pointer-addressable, so this usually signals a deeply unusual path.
Related errors
- conversion error for field %s: %s
- unable to convert from Quantity %v to float
- BuildFlagsList to reflect value: %s
- only accepts structs; got %T
- cannot set field %q (marked immutable)
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/fd8182905e515ea1.
Report an issue: GitHub.