gohugoio/hugo · error
is an unexported method of type
Error message
%s is an unexported method of type %s
What it means
Thrown by Hugo's `where` template function while resolving the key against each element. `evaluateSubElem` (tpl/collections/where.go:328-341) first looks up a method by name; if it finds one but the method's reflect PkgPath is non-empty (i.e. its first letter is lowercase / unexported), it refuses to call it. Go's reflect package cannot invoke unexported methods, so Hugo surfaces the name and type so you can correct the template key.
Solutions
- Use the exported (capitalized) method name as the where key, e.g. `CountItems` instead of `countItems`.
- If no exported equivalent exists, add a thin exported accessor method on the element type.
- Verify the method name against the type with `reflect.TypeFor[T]().Method(i).Name` or the package docs.
Example fix
// before
{{ where $items "countItems" "==" 3 }}
// after
{{ where $items "CountItems" "==" 3 }} Defensive patterns
Strategy: validation
Validate before calling
{{/* Verify the name resolves to an exported method before filtering */}}
{{ $t := reflect.TypeOf (index $items 0) }}
{{ $ok := false }}
{{ range $i := seq (len $t.NumMethod false) }}
{{ $m := $t.Method $i }}
{{ if eq $m.Name $key }}{{ $ok = true }}{{ end }}
{{ end }} Prevention
- Only reference exported (capitalized) methods in `where` keys.
- Keep a list of template-visible exported method names in your model docs.
- After renaming a method, grep templates for the old lowercase spelling.
When it happens
Trigger: Calling `{{ where $items "privateHelper" "==" x }}` where the element type has a method `func (t) privateHelper() ...` (lowercase). Also reachable via a dotted key path like `.foo.privateHelper` where a path segment names an unexported method.
Common situations: Template author guesses a method name by guessing lowercase Go conventions; an embedded/internal type exposes only a private helper that the public API wraps; refactoring renames a method to lowercase.
Related errors
- is an unexported field of struct type
- can't evaluate a nil pointer of type
- error at calling a method
- is a method of type but only returns an error type
- is a method of type but requires more than parameter
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/4607a68461f34cf3.
Report an issue: GitHub.
Appendix: source
Thrown at tpl/collections/where.go:341
objPtr := obj
if !hreflect.IsInterfaceOrPointer(objPtr.Kind()) && objPtr.CanAddr() {
objPtr = objPtr.Addr()
}
mt := hreflect.GetMethodByNameForType(objPtr.Type(), elemName)
if mt.Func.IsValid() {
// Receiver is the first argument.
args := []reflect.Value{objPtr}
num := mt.Type.NumIn()
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
}
View on GitHub (pinned to 52c9bd7908)