twpayne/chezmoi · error

expected a []string, got a %T

Error message

expected a []string, got a %T

What it means

This map-to-EntryTypeSet decode hook expects JSON/YAML config data to be a []any that it will narrow to strings. If the whole value is not a []any (e.g. a string, map, or bool), the hook fails with this message showing the actual Go type. It exists to convert list-style entry type config values into EntryTypeSet.

Source

Thrown at internal/chezmoi/entrytypeset.go:355

	return strings.Join(entryTypeStrs, ",")
}

// Type implements github.com/spf13/pflag.Value.Type.
func (s *EntryTypeSet) Type() string {
	return "types"
}

// StringSliceToEntryTypeSetHookFunc is a
// github.com/go-viper/mapstructure/v2.DecodeHookFunc that parses an
// EntryTypeSet from a []string.
func StringSliceToEntryTypeSetHookFunc() mapstructure.DecodeHookFunc {
	return func(from, to reflect.Type, data any) (any, error) {
		if to != reflect.TypeFor[EntryTypeSet]() {
			return data, nil
		}
		elemsAny, ok := data.([]any)
		if !ok {
			return nil, fmt.Errorf("expected a []string, got a %T", data)
		}
		elemStrs := make([]string, len(elemsAny))
		for i, elemAny := range elemsAny {
			elemStr, ok := elemAny.(string)
			if !ok {
				return nil, fmt.Errorf("expected a []string, got a %T element", elemAny)
			}
			elemStrs[i] = elemStr
		}
		s := NewEntryTypeSet(EntryTypesNone)
		if err := s.SetSlice(elemStrs); err != nil {
			return nil, err
		}
		return s, nil
	}
}

// EntryTypeSetFlagCompletionFunc completes EntryTypeSet flags.

View on GitHub (pinned to f901167e46)

Solutions

  1. Wrap the value in a list: use ["file"] instead of "file"
  2. Check the config schema for the field and match the expected []string type
  3. If building data programmatically, pass []any{...} of strings into the decode path

Example fix

// before (config)
mode = "file"

// after
mode = ["file"]
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := value.([]any); !ok {
    return fmt.Errorf("entry type config must be a list")
}

Type guard

func isStringSlice(v any) bool {
    elems, ok := v.([]any)
    if !ok { return false }
    for _, e := range elems {
        if _, ok := e.(string); !ok { return false }
    }
    return true
}

Try / catch

var out EntryTypeSet
if err := decode(data, &out); err != nil {
    if strings.Contains(err.Error(), "expected a []string") {
        // wrap scalar into []any{scalar} and retry
    }
}

Prevention

When it happens

Trigger: A config value that should be a list of entry type strings (e.g. in .chezmoi.toml or a decode hook target) is instead a scalar or object — a single quoted string rather than an array.

Common situations: Writing mode = "file" instead of mode = ["file"] in config, YAML collapsing a one-element list incorrectly, or passing the wrong field type when programmatically building config.

Related errors


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/4fad1334cb52b5a7. Report an issue: GitHub.