gohugoio/hugo · error

failed to convert %T to a string slice

Error message

failed to convert %T to a string slice

What it means

`types.ToStringSlicePreserveStringE` converts a value to `[]string`, wrapping single strings, delegating to `cast.ToStringSliceE`, and finally reflecting over slices/arrays element-by-element. If the value is neither a string nor any slice/array kind (e.g. a map, number, or struct), the reflect fallback hits its default case and returns this error naming the concrete Go type.

Source

Thrown at common/types/convert.go:81

		return result, nil
	}

	// Probably []int or similar. Fall back to reflect.
	vv := reflect.ValueOf(v)

	switch vv.Kind() {
	case reflect.Slice, reflect.Array:
		result = make([]string, vv.Len())
		for i := range vv.Len() {
			s, err := cast.ToStringE(vv.Index(i).Interface())
			if err != nil {
				return nil, err
			}
			result[i] = s
		}
		return result, nil
	default:
		return nil, fmt.Errorf("failed to convert %T to a string slice", v)
	}
}

// TypeToString converts v to a string if it's a valid string type.
// Note that this will not try to convert numeric values etc.,
// use ToString for that.
func TypeToString(v any) (string, bool) {
	switch s := v.(type) {
	case string:
		return s, true
	case template.HTML:
		return string(s), true
	case template.CSS:
		return string(s), true
	case template.HTMLAttr:
		return string(s), true
	case template.JS:
		return string(s), true

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Change the value to a string or an array of strings in the config/front matter identified by the printed type (e.g. map[string]interface{} means you wrote a table/map).
  2. In templates, check the argument you pass — use `slice "a" "b"` or a field that is actually a string list.
  3. Validate front matter of recently edited content: `hugo --printPathWarnings` and check the file named in the surrounding error context.

Example fix

# before (front matter)
tags:
  name: go

# after
tags:
  - go
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := v.([]string); !ok {
    if _, ok := v.([]any); !ok {
        return fmt.Errorf("expected string slice, got %T", v)
    }
}

Type guard

func toStringSlice(v any) ([]string, bool) {
    switch t := v.(type) {
    case []string:
        return t, true
    case []any:
        out := make([]string, 0, len(t))
        for _, e := range t {
            s, ok := e.(string)
            if !ok { return nil, false }
            out = append(out, s)
        }
        return out, true
    case string:
        return []string{t}, true
    }
    return nil, false
}

Prevention

When it happens

Trigger: Config fields or template function arguments that must be a string or list of strings — e.g. taxonomy terms, `keywords`, cascade/permalink lists, `where`/`in` inputs — receiving a map or scalar of the wrong shape; also slices containing elements that can't stringify (the inner cast error propagates).

Common situations: YAML front matter where `tags: foo: bar` (a map) was written instead of `tags: [foo, bar]`; passing a page or dict object where a list of strings is expected in a template; config merging producing a table where an array was expected.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/9a7654913c9d6220.json. Report an issue: GitHub ↗.