gohugoio/hugo · error
%s is an unexported field of struct type %s
Error message
%s is an unexported field of struct type %s
What it means
In the struct branch of `evaluateSubElem` (where.go:368-376), after `FieldByName` succeeds Hugo checks `ft.PkgPath != "" && !ft.Anonymous`. A non-empty PkgPath means the field is unexported (lowercase); reflect cannot read it, so this error names the field and struct type.
Source
Thrown at tpl/collections/where.go:372
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)
if ok {
if ft.PkgPath != "" && !ft.Anonymous {
return zero, fmt.Errorf("%s is an unexported field of struct type %s", elemName, typ)
}
return obj.FieldByIndex(ft.Index), nil
}
return zero, fmt.Errorf("%s isn't a field of struct type %s", elemName, typ)
case reflect.Map:
kv := reflect.ValueOf(elemName)
if kv.Type().AssignableTo(obj.Type().Key()) {
return obj.MapIndex(kv), nil
}
return zero, fmt.Errorf("%s isn't a key of map type %s", elemName, typ)
}
return zero, fmt.Errorf("%s is neither a struct field, a method nor a map element of type %s", elemName, typ)
}
// parseWhereArgs parses the end arguments to the where function. Return a
// match value and an operator, if one is defined.
func parseWhereArgs(args ...any) (mv reflect.Value, op string, err error) {
switch len(args) {View on GitHub (pinned to 52c9bd7908)
Solutions
- Use the exported field name (capitalized first letter) as the where key.
- If the field is genuinely private, expose it via an exported field or a method returning a value.
- Check the type definition for the exact exported field name.
Example fix
// before
{{ where $pages "draft" "==" true }}
// after
{{ where $pages "Draft" "==" true }} Defensive patterns
Strategy: validation
Validate before calling
// Go-side: confirm the field is exported and present before templating ft, ok := reflect.TypeOf(item).FieldByName(fieldName) ok = ok && ft.PkgPath == "" || ft.Anonymous
Prevention
- Always capitalize the first letter of fields used in templates.
- Keep a documented list of exported fields per model.
- Add a Go test asserting template-referenced fields are exported.
When it happens
Trigger: `{{ where $pages "draft" "==" true }}` against a struct whose field is `draft bool` (lowercase) rather than the exported `Draft bool`.
Common situations: Guessing a lowercase field name from the source; a Go struct with private fields that the public API exposes via accessors; confusion between internal struct fields and the JSON/template-visible ones.
Related errors
- %s is an unexported method of type %s
- %s isn't a field of struct type %s
- %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
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/6e947910045a246c.
Report an issue: GitHub.