gohugoio/hugo · error
unable to process menus for page
Error message
unable to process menus for page %q: %w
What it means
Thrown by PageMenusFromPage (navigation/pagemenus.go:73) via the wrapErr closure when the page's front-matter 'menu' value cannot be interpreted as a string, a slice of strings, or a map of menu entries. The %w wraps the underlying error from hmaps.ToStringMapE (the value is not a map) or mapstructure.WeakDecode (a menu entry field has the wrong type). It names the offending page via p.Path().
Solutions
- Open the page named in the error message and fix its front-matter 'menu' field to be a string, a list of strings, or a map of menu-entry objects.
- Validate nested menu entries are maps with recognized keys (name, weight, parent, identifier, url, pre, post).
- Run hugo --templateMetricsHints or a local build to confirm the error clears after the edit.
- If the value is intentionally dynamic, move it into a valid structure under [menus] in the site config instead of page front matter.
Example fix
# before (invalid nested type)
---
menu:
main: 42
---
# after
---
menu:
main:
name: "About"
weight: 1
--- Defensive patterns
Strategy: validation
Validate before calling
// Validate page front-matter 'menu' before passing to PageMenusFromPage.
func validateMenuValue(ms any) error {
switch v := ms.(type) {
case string, []string, []any, map[string]any:
return nil
default:
return fmt.Errorf("menu must be string, []string, or map; got %T", v)
}
} Type guard
func isValidMenuShape(ms any) bool {
switch ms.(type) {
case string, []string, []any, map[string]any, nil:
return true
}
return false
} Try / catch
pm, err := navigation.PageMenusFromPage(ms, p)
if err != nil {
// err already wraps the page path; log and skip this page's menus.
hugrot.Printf("skipping menus for %s: %v", p.Path(), err)
} Prevention
- Use Hugo's documented menu front-matter shapes only: string, list of strings, or a map of entry maps.
- Validate front matter with a schema/lint step in CI.
- When migrating front-matter formats, re-check nested menu tables.
When it happens
Trigger: A content file sets menu in front matter to an unsupported shape — e.g. menu = 123 (scalar number), menu = [["a"],["b"]] (nested slice), or a map whose nested entry is not itself a map (menu.main = "just-a-string-but-also-not". structured incorrectly). The cast.ToStringE and cast.ToStringSliceE both fail, then hmaps.ToStringMapE(ms) or ToStringMapE(menu) fails, triggering wrapErr.
Common situations: Switching front-matter format (YAML to TOML) and forgetting to adjust the menu table syntax; copy-pasting a menu config example that uses a different structure; providing menu: { main: [1,2,3] } where a nested object is expected; using a numeric or boolean value where a string/menu-map is required.
Related errors
- duplicate menu entry with identifier
- failed to convert metadata for file
- failed to decode page map
- failed to decode sites from front matter
- failed to parse file
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/daea2e2163a816a7.
Report an issue: GitHub.
Appendix: source
Thrown at navigation/pagemenus.go:73
if err == nil {
me.Menu = mname
pm[mname] = &me
return pm, nil
}
// Could be a slice of strings
mnames, err := cast.ToStringSliceE(ms)
if err == nil {
for _, mname := range mnames {
me.Menu = mname
pm[mname] = &me
}
return pm, nil
}
wrapErr := func(err error) error {
return fmt.Errorf("unable to process menus for page %q: %w", p.Path(), err)
}
// Could be a structured menu entry
menus, err := hmaps.ToStringMapE(ms)
if err != nil {
return pm, wrapErr(err)
}
for name, menu := range menus {
menuEntry := MenuEntry{Menu: name}
if menu != nil {
ime, err := hmaps.ToStringMapE(menu)
if err != nil {
return pm, wrapErr(err)
}
if err := mapstructure.WeakDecode(ime, &menuEntry.MenuConfig); err != nil {
return pm, err
}View on GitHub (pinned to 52c9bd7908)