{"record":{"id":"a85577de4259e28a","repo":"OrchardCMS/OrchardCore","slug":"the-shape-metadata-type-shape-has-been-called-recursively","errorCode":null,"errorMessage":"The '{shape.Metadata.Type}' shape has been called recursively more than {LiquidTemplateContext.MaxShapeRecursions} times.","messagePattern":"The '(.+?)' shape has been called recursively more than (.+?) times\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.DisplayManagement.Liquid/LiquidViewTemplate.cs","lineNumber":242,"sourceCode":"    {\n        await context.InitializeAsync(viewContext);\n\n        context.EnterChildScope();\n\n        var viewLocalizer = context.Services.GetRequiredService<IViewLocalizer>();\n\n        if (viewLocalizer is IViewContextAware contextable)\n        {\n            contextable.Contextualize(viewContext);\n        }\n\n        context.SetValue(\"ViewLocalizer\", new ObjectValue(viewLocalizer));\n\n        if (context.GetValue(\"Model\")?.ToObjectValue() == model && model is IShape shape)\n        {\n            if (context.ShapeRecursions++ > LiquidTemplateContext.MaxShapeRecursions)\n            {\n                throw new InvalidOperationException(\n                    $\"The '{shape.Metadata.Type}' shape has been called recursively more than {LiquidTemplateContext.MaxShapeRecursions} times.\");\n            }\n        }\n        else\n        {\n            context.ShapeRecursions = 0;\n        }\n\n        context.SetValue(\"Model\", model);\n    }\n}\n","sourceCodeStart":224,"sourceCodeEnd":254,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.DisplayManagement.Liquid/LiquidViewTemplate.cs#L224-L254","documentation":"LiquidViewTemplate.EnterScopeAsync tracks shape recursion depth in the Liquid context. When the same IShape instance is entered again while already the current Model and the recursion counter exceeds LiquidTemplateContext.MaxShapeRecursions, an InvalidOperationException is thrown. This prevents infinite shape-recursion (stack overflow / endless rendering) from self-referencing shapes or templates.","triggerScenarios":"A Liquid template for shape X renders shape X again (directly or via a wrapper/alternate loop), or a shape's display triggers its own template repeatedly (e.g. a list shape containing itself, or a menu item whose template re-displays the parent menu) more than MaxShapeRecursions times.","commonSituations":"Self-referencing content (a content item linked as its own child), misconfigured menu/term structures with cycles, Liquid templates using {{ Model | shape_render }} on the same shape recursively, placement causes a shape to include itself.","solutions":["Break the recursion in the template: render child shapes, not the shape itself","Fix the data cycle (self-referencing content item, menu, or taxonomy)","If depth is legitimately deep, raise the recursion limit configuration for LiquidTemplateContext","Use alternates so nested shapes use a different template and stop re-entering the same scope"],"exampleFix":"// before\n{{ Model.Items | shape_render }} <!-- template renders itself for each item that is the same shape -->\n// after\n{% for item in Model.Items %}\n    {% assign child = item | shape_properties %}\n    {% render_shape child.ChildShape %} <!-- render distinct child shape type -->\n{% endfor %}","handlingStrategy":"validation","validationCode":"// before rendering a shape in Liquid, ensure it is not the same instance as the current Model\n{% if Model.Handle != shape.Handle %}\n    {% render_shape shape %}\n{% endif %}","typeGuard":null,"tryCatchPattern":"try { await template.RenderAsync(...); } catch (InvalidOperationException e) when (e.Message.Contains(\"recursively\")) { _logger.LogError(e, \"Shape recursion detected: {0}\", e.Message); }","preventionTips":["Avoid templates that render their own shape type recursively","Check content structures (menus, taxonomies, lists) for cycles","Keep alternates for nested levels so a different template handles children","Increase MaxShapeRecursions only after confirming the recursion is intentional and bounded"],"tags":["liquid","shapes","recursion","infinite-loop"],"backgroundTag":"shape-recursion-limit","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}