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

  1. Ensure the target object is passed as a pointer (*api.Cluster / *api.InstanceGroup)
  2. Choose a different field path that traverses only exported, pointer-addressable fields
  3. 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

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


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