Shopify/liquid · error · Liquid::StackLevelError

Nesting too deep

Error message

Nesting too deep

What it means

Liquid raises StackLevelError via Context#check_overflow when the template's block nesting exceeds Block::MAX_DEPTH. This guards against runaway recursion in templates (e.g. deeply nested for/if blocks or recursive includes) that would otherwise blow the Ruby stack. It is a protective limit, not a bug in your template data.

Solutions

  1. Find and break the recursion or reduce nesting depth in the template (often a self-including partial).
  2. If your use case legitimately needs more depth, raise Block::MAX_DEPTH in an initializer (accepting deeper Ruby stack usage).
  3. Set a depth/size limit on user-supplied templates before parsing.
  4. Wrap render in error handling and report the offending template to the user.

Example fix

// before
{% include 'wrapper' %}  # wrapper.liquid contains {% include 'wrapper' %}
// after
{% include 'wrapper' %}  # wrapper.liquid contains real content, no self-include
Defensive patterns

Strategy: validation

Validate before calling

def template_depth_ok?(source)
  max_nesting = 0; cur = 0
  source.scan(/\{%-?\s*(if|unless|case|for|tablerow|capture|include)\b/) { cur += 1; max_nesting = [max_nesting, cur].max }
  source.scan(/\{%-?\s*end\w+/) { cur -= 1 }
  max_nesting <= 50 # keep below Block::MAX_DEPTH
end

Type guard

def safe_parse?(source)
  Liquid::Template.parse(source)
  true
rescue Liquid::StackLevelError
  false
end

Try / catch

begin
  Liquid::Template.parse(src).render(ctx)
rescue Liquid::StackLevelError => e
  logger.warn("Template nesting too deep: #{e.message}")
  render_fallback_template
end

Prevention

When it happens

Trigger: Parsing or rendering a template whose nested block depth (base_scope_depth + scopes) exceeds Block::MAX_DEPTH — e.g. an {% include %} that includes itself, or hundreds of nested {% for %}/{% if %} blocks.

Common situations: Recursive or mutually-recursive includes/partials generated programmatically; templating tools that build deeply nested DOM wrappers; user-supplied templates crafted (maliciously or accidentally) to nest deeply.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08). Data as JSON: /api/errors/8eafe92784da0040. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/context.rb:288

    def try_variable_find_in_environments(key, raise_on_not_found:)
      @environments.each do |environment|
        found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
        if !found_variable.nil? || @strict_variables && raise_on_not_found
          return found_variable
        end
      end
      @static_environments.each do |environment|
        found_variable = lookup_and_evaluate(environment, key, raise_on_not_found: raise_on_not_found)
        if !found_variable.nil? || @strict_variables && raise_on_not_found
          return found_variable
        end
      end
      nil
    end

    def check_overflow
      raise StackLevelError, "Nesting too deep" if overflow?
    end

    def overflow?
      base_scope_depth + @scopes.length > Block::MAX_DEPTH
    end

    def internal_error
      # raise and catch to set backtrace and cause on exception
      raise Liquid::InternalError, 'internal'
    rescue Liquid::InternalError => exc
      exc
    end

    def squash_instance_assigns_with_environments
      @scopes.last.each_key do |k|
        @environments.each do |env|
          if env.key?(k)
            scopes.last[k] = lookup_and_evaluate(env, k)

View on GitHub (pinned to 807d45a6b3)