gohugoio/hugo · error

%s is a method of type %s returning two values but the secon

Error message

%s is a method of type %s returning two values but the second value is not an error type

What it means

`evaluateSubElem` (where.go:350-351) allows two-return methods only when the second return is an `error`. If a method returns two values but `mt.Type.Out(1)` does not implement the error interface (e.g. `(string, int)`), Hugo cannot disambiguate which value to use and reports it.

Source

Thrown at tpl/collections/where.go:351

		maxNumIn := 1
		if num > 1 && hreflect.IsContextType(mt.Type.In(1)) {
			args = append(args, ctx)
			maxNumIn = 2
		}

		switch {
		case mt.PkgPath != "":
			return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
		case mt.Type.NumIn() > maxNumIn:
			return zero, fmt.Errorf("%s is a method of type %s but requires more than %d parameter", elemName, typ, maxNumIn)
		case mt.Type.NumOut() == 0:
			return zero, fmt.Errorf("%s is a method of type %s but returns no output", elemName, typ)
		case mt.Type.NumOut() > 2:
			return zero, fmt.Errorf("%s is a method of type %s but returns more than 2 outputs", elemName, typ)
		case mt.Type.NumOut() == 1 && mt.Type.Out(0).Implements(errorType):
			return zero, fmt.Errorf("%s is a method of type %s but only returns an error type", elemName, typ)
		case mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType):
			return zero, fmt.Errorf("%s is a method of type %s returning two values but the second value is not an error type", elemName, typ)
		}
		res := mt.Func.Call(args)
		if len(res) == 2 && !res[1].IsNil() {
			return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
		}
		return res[0], nil
	}

	// elemName isn't a method so next start to check whether it is
	// a struct field or a map value. In both cases, it mustn't be
	// a nil value
	if isNil {
		return zero, fmt.Errorf("can't evaluate a nil pointer of type %s by a struct field or map key name %s", typ, elemName)
	}
	obj = reflect.Indirect(obj)
	switch obj.Kind() {
	case reflect.Struct:
		ft, ok := obj.Type().FieldByName(elemName)

View on GitHub (pinned to 52c9bd7908)

Solutions

  1. Change the method signature to `(T, error)` to match Hugo's expected idiom.
  2. Provide a dedicated single-value method for the field you want to match.
  3. Filter on a struct field that already exposes the desired value.

Example fix

// before
{{ where $items "Get" "==" "x" }}  {{/* Get() (string, int) */}}
// after
{{ where $items "Value" "==" "x" }}  {{/* Value() string */}}
Defensive patterns

Strategy: validation

Validate before calling

// Go-side: two returns must be (T, error)
mt, ok := reflect.TypeOf(item).MethodByName(name)
if ok && mt.Type.NumOut() == 2 {
    ok = mt.Type.Out(1).Implements(reflect.TypeFor[error]())
}

Prevention

When it happens

Trigger: `{{ where $items "Get" "==" "x" }}` against `func (i) Get() (string, int)`.

Common situations: Reusing a tuple-returning accessor as a where key; a method that returns `(value, count)` or `(value, ok bool)` rather than `(value, error)`.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/13f8ea603bcae860. Report an issue: GitHub.