gohugoio/hugo · error

unable to convert value of type %q to %q

Error message

unable to convert value of type %q to %q

What it means

When Hugo's collection functions (append, union, intersect, sort comparisons, etc.) mix element types, `convertValue`/`convertNumber` try to coerce a value to the target slice's element type via reflection. If the numeric conversion is not possible (e.g. the value would not fit or the kinds are incompatible per `hreflect.ConvertIfPossible`), Hugo reports the source and destination types in this error.

Source

Thrown at tpl/collections/reflect_helpers.go:100

func convertValue(v reflect.Value, to reflect.Type) (reflect.Value, error) {
	if v.Type().AssignableTo(to) {
		return v, nil
	}
	switch kind := to.Kind(); {
	case kind == reflect.String:
		return hreflect.ToStringValueE(v)
	case hreflect.IsNumber(kind):
		return convertNumber(v, to)
	default:
		return reflect.Value{}, fmt.Errorf("%s is not assignable to %s", v.Type(), to)
	}
}

func convertNumber(v reflect.Value, typ reflect.Type) (reflect.Value, error) {
	if v, ok := hreflect.ConvertIfPossible(v, typ); ok {
		return v, nil
	}
	return reflect.Value{}, fmt.Errorf("unable to convert value of type %q to %q", v.Type().String(), typ.String())
}

func newSliceElement(items any) any {
	tp := reflect.TypeOf(items)
	if tp == nil {
		return nil
	}
	switch tp.Kind() {
	case reflect.Array, reflect.Slice:
		tp = tp.Elem()
		if tp.Kind() == reflect.Pointer {
			tp = tp.Elem()
		}

		return reflect.New(tp).Interface()
	}
	return nil
}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Normalize types before combining: cast values explicitly with `int`, `float`, or `string` so both operands share a type, e.g. `{{ $s = $s | append (int $v) }}`.
  2. Build the slice from scratch with `slice` using consistently typed values instead of appending mixed types into an existing typed slice.
  3. If the data comes from JSON/YAML, remember numbers may be float64 — cast at the point of use.

Example fix

<!-- before -->
{{ $nums := site.Params.counts }}{{ $nums = $nums | append "7" }}
<!-- after -->
{{ $nums := site.Params.counts }}{{ $nums = $nums | append (int "7") }}
Defensive patterns

Strategy: type-guard

Validate before calling

{{ $a := slice 1 2 }}
{{ $b := slice "3" "4" }}
{{ if eq (printf "%T" (index $a 0)) (printf "%T" (index $b 0)) }}{{ union $a $b }}{{ end }}

Type guard

{{ $sameType := eq (printf "%T" $x) (printf "%T" $y) }}

Try / catch

{{ with try (intersect $a $b) }}{{ with .Err }}{{ warnf "type mismatch in collection op: %s" . }}{{ else }}{{ .Value }}{{ end }}{{ end }}

Prevention

When it happens

Trigger: Appending or set-combining values into a typed slice whose element type the value cannot convert to — e.g. appending a negative int or a float into a `[]uint`, or mixing incompatible numeric types where lossless conversion fails.

Common situations: Mixing data-file values (JSON numbers decode as float64) with typed slices built in templates or from Go-side params; appending template-computed ints into slices originating from config with a narrower/unsigned type; version changes that tightened type handling in collections functions.

Related errors


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