gohugoio/hugo · critical
maximum template call stack size exceeded in %q
Error message
maximum template call stack size exceeded in %q
What it means
Hugo tracks template nesting depth per execution via `CurrentTemplateInfo.Level` in `TemplateStore.ExecuteWithContextAndKey`. When a template invocation chain exceeds 999 levels, Hugo aborts with this error naming the template file, since it almost certainly indicates infinite recursion between templates/partials rather than legitimate nesting.
Source
Thrown at tpl/tplimpl/templatestore.go:541
templ := ti.Template
parent := tpl.Context.CurrentTemplate.Get(ctx)
var level int
if parent != nil {
level = parent.Level + 1
}
currentTi := &tpl.CurrentTemplateInfo{
Parent: parent,
Level: level,
Key: key,
CurrentTemplateInfoOps: ti,
}
ctx = tpl.Context.CurrentTemplate.Set(ctx, currentTi)
const levelThreshold = 999
if level > levelThreshold {
return fmt.Errorf("maximum template call stack size exceeded in %q", ti.Filename())
}
if t.opts.Metrics != nil {
defer t.opts.Metrics.MeasureSince(templ.Name(), time.Now())
}
execErr := t.storeSite.executer.ExecuteWithContext(ctx, ti, wr, data)
if execErr != nil {
return t.addFileContext(ti, "execute of template failed", execErr)
}
return nil
}
func (t *TemplateStore) GetFunc(name string) (reflect.Value, bool) {
v, found := t.storeSite.execHelper.funcs[name]
return v, found
}
View on GitHub ↗ (pinned to 8a468df065)
Solutions
- Open the template named in the error and trace its `partial`/`template` calls for a cycle back to itself; add a terminating condition (e.g. only recurse when `.Children` is non-empty).
- Pass a depth counter in the partial context (`dict "depth" (add .depth 1)`) and stop at a sane limit.
- Check render hooks (layouts/_markup/*) that call `.RenderString` or partials which re-enter the same hook.
Example fix
<!-- before: partials/menu.html -->
{{ range .entries }}{{ partial "menu.html" (dict "entries" .Children) }}{{ end }}
<!-- after -->
{{ range .entries }}{{ with .Children }}{{ partial "menu.html" (dict "entries" .) }}{{ end }}{{ end }} Defensive patterns
Strategy: validation
Validate before calling
{{/* pass an explicit depth guard to recursive partials */}}
{{ $depth := .depth | default 0 }}
{{ if lt $depth 50 }}{{ partial "tree.html" (dict "node" .node "depth" (add $depth 1)) }}{{ end }} Prevention
- Add an explicit depth counter to any recursive partial or shortcode and stop before Hugo's stack limit
- Never have a template include itself (or a cycle of includes) without a terminating condition
- Check that menu/section trees used for recursion are acyclic
- Prefer iteration with a stack (slice) over deep template recursion for large trees
When it happens
Trigger: A partial that includes itself (directly or via a cycle: A includes B includes A) without a terminating condition; recursive `partial`/`template` calls on nested data structures (e.g. menu trees) whose recursion never bottoms out; a baseof/block cycle.
Common situations: Recursive menu or table-of-contents partials missing the base-case check (e.g. forgetting `{{ if .Children }}` before re-invoking); a partial renamed such that it accidentally calls itself instead of a differently-named helper; render hooks that render markdown which re-triggers the same hook.
Related errors
- circular call stack detected in partial %q
- partial %q not found
- inner cannot be used inside a with block that wraps a partia
- can't apply myself (no turtles allowed)
- text is already rendered, repeating it may cause infinite re
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/59825a66bbef044d.json.
Report an issue: GitHub ↗.