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
- Change the method signature to `(T, error)` to match Hugo's expected idiom.
- Provide a dedicated single-value method for the field you want to match.
- 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
- Adopt the Go idiom (value, error) for two-return template methods.
- Avoid (value, ok bool) accessors for template-facing APIs.
- Add a lint/test enforcing the (T, error) shape on template methods.
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
- %s is a method of type %s but requires more than %d paramete
- %s is a method of type %s but returns no output
- %s is a method of type %s but returns more than 2 outputs
- %s is a method of type %s but only returns an error type
- %s is an unexported method of type %s
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/13f8ea603bcae860.
Report an issue: GitHub.