{"record":{"id":"d0284b89f20b579e","repo":"gohugoio/hugo","slug":"circular-call-stack-detected-in-partial-q","errorCode":null,"errorMessage":"circular call stack detected in partial %q","messagePattern":"circular call stack detected in partial %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tpl/partials/partials.go","lineNumber":223,"sourceCode":"func (ns *Namespace) IncludeCached(ctx context.Context, name string, context any, variants ...any) (any, error) {\n\tstart := time.Now()\n\tkey := partialCacheKey{\n\t\tName:     name,\n\t\tVariants: variants,\n\t}\n\tkeyString := key.Key()\n\n\tdepsManagerIn := tpl.Context.GetDependencyManagerInCurrentScope(ctx)\n\tti, err := ns.lookup(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif parent := tpl.Context.CurrentTemplate.Get(ctx); parent != nil {\n\t\tfor parent != nil {\n\t\t\tif parent.CurrentTemplateInfoOps == ti && parent.Key == keyString {\n\t\t\t\t// This will deadlock if we continue.\n\t\t\t\treturn nil, fmt.Errorf(\"circular call stack detected in partial %q\", ti.Filename())\n\t\t\t}\n\t\t\tparent = parent.Parent\n\t\t}\n\t}\n\n\tr, found, err := ns.cachedPartials.cache.GetOrCreate(keyString, func(string) (includeResult, error) {\n\t\tvar depsManagerShared identity.Manager\n\t\tif ns.deps.Conf.Watching() {\n\t\t\t// We need to create a shared dependency manager to pass downwards\n\t\t\t// and add those same dependencies to any cached invocation of this partial.\n\t\t\tdepsManagerShared = identity.NewManager()\n\t\t\tctx = tpl.Context.DependencyManagerScopedProvider.Set(ctx, depsManagerShared.(identity.DependencyManagerScopedProvider))\n\t\t}\n\t\t// Mark the ctx so templates.Defer can reject being called from a cached body.\n\t\tctx = tpl.Context.IsInPartialCached.Set(ctx, true)\n\t\tr := ns.doInclude(ctx, keyString, ti, context)\n\t\tif ns.deps.Conf.Watching() {\n\t\t\tr.mangager = depsManagerShared","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/gohugoio/hugo/blob/52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2/tpl/partials/partials.go#L205-L241","documentation":"Thrown by IncludeCached when walking the CurrentTemplate chain reveals that the same partialCached with the same cache key is already an ancestor in the call stack. Continuing would deadlock because GetOrCreate on lazycache blocks on an in-progress entry.","triggerScenarios":"A partial invoked via partialCached calls itself (directly or via another partialCached) with the same variants, creating a cycle: partial A is cached and calls partialCached A. Detected by comparing parent.CurrentTemplateInfoOps and parent.Key against the current invocation.","commonSituations":"Recursive partials (e.g. rendering a tree/menu where a node partial calls partialCached on child nodes with the same name), refactoring a partial to use partialCached without removing a self-reference, or variant values that collide to the same cache key.","solutions":["Use plain partial (not partialCached) for the recursive call so the cache is not involved.","Break the cycle by splitting the recursive part into a separate non-cached partial.","Ensure the variants differentiate recursive invocations if caching is genuinely needed per level.","Render tree structures with a different pattern (e.g. a single partial iterating over children) instead of recursive cached calls."],"exampleFix":"// before (recursive cached self-call):\n{{ partialCached \"menu.html\" . }}\n// after (use plain partial for recursion):\n{{ partial \"menu.html\" . }}","handlingStrategy":"validation","validationCode":"{{ if not (eq $.PartialName \"menu.html\") }}\n  {{ partialCached \"menu.html\" . }}\n{{ else }}\n  {{ partial \"menu.html\" . }}\n{{ end }}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never call partialCached from within the partial it caches.","Use plain partial for recursive structures (trees, menus).","Give cached variants distinct values so cache keys differ across recursion levels."],"tags":["partials","partialcached","recursion","deadlock","cycle"],"backgroundTag":null,"analyzedSha":"52c9bd7908b4d02d4d0ff8f82a888834d6ee10d2","analyzedAt":"2026-08-09T21:49:36.660Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}