kubernetes/kops · error

only accepts structs; got %T

Error message

only accepts structs; got %T

What it means

getValueFromStruct walks a dotted key path through a struct using reflection, dereferencing pointers along the way. If, after dereferencing, the current value is not a struct (and cannot be indexed further by field name), it returns this error. It exists because FieldByName only works on struct kinds.

Source

Thrown at pkg/configbuilder/buildconfigfile.go:103

	if err != nil {
		return nil, err
	}

	return configFile, nil
}

func getValueFromStruct(keyWithDots string, object interface{}) (*reflect.Value, error) {
	keySlice := strings.Split(keyWithDots, ".")
	v := reflect.ValueOf(object)
	// iterate through field names, ignoring the first name as it might be the current instance name
	// you can make it recursive also if want to support types like slice,map etc along with struct
	for _, key := range keySlice {
		for v.Kind() == reflect.Ptr {
			v = v.Elem()
		}
		// we only accept structs
		if v.Kind() != reflect.Struct {
			return nil, fmt.Errorf("only accepts structs; got %T", v)
		}
		v = v.FieldByName(key)
	}

	return &v, nil
}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Verify each segment of the dotted tag path resolves to a struct until the final segment
  2. Update the configfile tag to match the current (possibly changed) shape of the target struct
  3. Ensure the `target` argument passed to BuildConfigYaml is a pointer to a struct, not a map or slice
  4. If the source field legitimately became a non-struct, remove or rework the tag since dotted traversal only supports nested structs

Example fix

// before
FeatureGates map[string]string `json:"featureGates,omitempty" configfile:"feature-gates.enabled"`
// after (maps are not traversable; address the struct directly or drop the tag)
FeatureGates map[string]string `json:"featureGates,omitempty" configfile:"-"`
Defensive patterns

Strategy: type-guard

Validate before calling

func isStructLike(x interface{}) bool {
	v := reflect.ValueOf(x)
	for v.Kind() == reflect.Ptr { v = v.Elem() }
	return v.Kind() == reflect.Struct
}

Type guard

func ensureStruct(v reflect.Value) (reflect.Value, error) {
	for v.Kind() == reflect.Ptr {
		if v.IsNil() { return v, fmt.Errorf("nil pointer in path") }
		v = v.Elem()
	}
	if v.Kind() != reflect.Struct {
		return v, fmt.Errorf("only accepts structs; got %s", v.Kind())
	}
	return v, nil
}

Try / catch

v, err := getValueFromStruct(key, obj)
if err != nil {
	// "only accepts structs; got %T" — check the segment of the dotted path that went off-struct
	return fmt.Errorf("field path %q invalid for target: %w", key, err)
}

Prevention

When it happens

Trigger: The dotted path in a configfile tag traverses into a map, slice, interface, or basic type before a remaining key is looked up (e.g. tag "foo.bar" where foo resolves to a map or a string); or the target passed to getValueFromStruct is not a struct/pointer-to-struct at all.

Common situations: A configfile tag path written for a nested struct now points at a field that upstream changed to a map or scalar; test cases (TestWrongStructField) exercising wrong field paths; passing a non-struct target struct into the config builder.

Related errors


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