gohugoio/hugo · critical

max depth exceeded

Error message

max depth exceeded

What it means

A panic raised by defaultConfigProvider.WalkParams when recursively walking the site's params/config tree exceeds 1000 nested levels. It is a guard against unbounded recursion — a legitimate Hugo config never approaches this depth, so hitting it almost always means the params tree contains a cycle or pathologically deep nesting.

Source

Thrown at config/defaultConfigProvider.go:252

}

func (c *defaultConfigProvider) Keys() []string {
	c.mu.RLock()
	defer c.mu.RUnlock()
	var keys []string
	for k := range c.root {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func (c *defaultConfigProvider) WalkParams(walkFn func(params ...hmaps.KeyParams) bool) {
	maxDepth := 1000
	var walk func(depth int, params ...hmaps.KeyParams)
	walk = func(depth int, params ...hmaps.KeyParams) {
		if depth > maxDepth {
			panic(errors.New("max depth exceeded"))
		}
		if walkFn(params...) {
			return
		}
		p1 := params[len(params)-1]
		i := len(params)
		for k, v := range p1.Params {
			if p2, ok := v.(hmaps.Params); ok {
				paramsplus1 := make([]hmaps.KeyParams, i+1)
				copy(paramsplus1, params)
				paramsplus1[i] = hmaps.KeyParams{Key: k, Params: p2}
				walk(depth+1, paramsplus1...)
			}
		}
	}
	walk(0, hmaps.KeyParams{Key: "", Params: c.root})
}

View on GitHub ↗ (pinned to 8a468df065)

Solutions

  1. Inspect site params for cyclic or absurdly deep nesting — especially any generated/imported config; flatten or break the cycle.
  2. Bisect by removing config mounts/themes/modules until the panic disappears to locate the offending source.
  3. If the params are reasonable and the panic persists, report it as a Hugo bug with a minimal reproducer, since 1000 levels indicates an internal cycle.
Defensive patterns

Strategy: validation

Validate before calling

// Guard against absurdly deep nesting before handing maps to the config provider
func depth(m map[string]any, d int) int {
    max := d
    for _, v := range m {
        if mm, ok := v.(maps.Params); ok {
            if dd := depth(mm, d+1); dd > max { max = dd }
        }
    }
    return max
}
if depth(params, 0) > 32 { return errors.New("config nesting too deep — flatten your params") }

Try / catch

if err := provider.SetDefaults(params); err != nil {
    if strings.Contains(err.Error(), "max depth exceeded") {
        // config maps nested beyond the provider's recursion limit — usually a self-referencing or generated structure
    }
    return err
}

Prevention

When it happens

Trigger: WalkParams recursing more than 1000 map levels deep — a params map that (directly or via module/theme config merging or programmatic mutation) contains a reference cycle, or genuinely 1000+ levels of nested maps in config/params.

Common situations: Generated or machine-produced config/data injected into site params with self-referencing structures; extreme auto-generated nesting in params files; a Hugo bug where config merging creates a cycle — worth reporting upstream since hand-written configs cannot realistically trigger this.

Related errors


AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31). Data as JSON: /data/errors/b38ec3a5382ca394.json. Report an issue: GitHub ↗.