Shopify/liquid · error · Liquid::MemoryError

Memory limits exceeded

Error message

Memory limits exceeded

What it means

Liquid::ResourceLimits raises Liquid::MemoryError via raise_limits_reached when a template exceeds configured resource limits (render length, allocation counts, variable or output sizes). It flags that the template consumed more resources than allowed and aborts rendering.

Solutions

  1. Raise the resource limits if they're too strict (template.resource_limits = { render_length_limit: ..., render_score_limit: ... })
  2. Refactor the template to reduce output size (paginate loops, truncate strings)
  3. Profile the template with resource limit tracking to find the offending construct and optimize it

Example fix

# before
template.render(assigns)
# after
template.resource_limits = Liquid::ResourceLimits.new(render_length_limit: 500_000)
template.render(assigns)
Defensive patterns

Strategy: try-catch

Validate before calling

limits = template.resource_limits
raise unless limits # ensure limits configured sensibly for template complexity

Try / catch

begin
  template.render(assigns)
rescue Liquid::MemoryError => e
  Rails.logger.warn("Template exceeded resource limits: #{template_name}")
  render_fallback_or_paginated_view
end

Prevention

When it happens

Trigger: Rendering a template whose output, variable sizes, or loop allocations exceed ResourceLimits (e.g. render_length_limit, render_score_limit, alloc_profiler budgets) set on Liquid::Template via resource_limits or a render call with limits.

Common situations: User-submitted or merchant themes with very large loops or huge string concatenations; rendering extremely large product/JSON dumps; lowering limits for sandboxed rendering and hitting them on legitimate templates.

Related errors


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

Appendix: source

Thrown at lib/liquid/resource_limits.rb:54

      raise_limits_reached if @assign_score_limit && @assign_score > @assign_score_limit
      raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
    end

    # update either render_length or assign_score based on whether or not the writes are captured
    def increment_write_score(output)
      if (last_captured = @last_capture_length)
        captured = output.bytesize
        increment = captured - last_captured
        @last_capture_length = captured
        increment_assign_score(increment)
      elsif @render_length_limit && output.bytesize > @render_length_limit
        raise_limits_reached
      end
    end

    def raise_limits_reached
      @reached_limit = true
      raise MemoryError, "Memory limits exceeded"
    end

    def reached?
      @reached_limit
    end

    def reset
      @reached_limit = false
      @last_capture_length = nil
      @render_score = @assign_score = 0
      raise_limits_reached if @cumulative_render_score_limit && @cumulative_render_score > @cumulative_render_score_limit
      raise_limits_reached if @cumulative_assign_score_limit && @cumulative_assign_score > @cumulative_assign_score_limit
    end

    def with_capture
      old_capture_length = @last_capture_length
      begin
        @last_capture_length = 0

View on GitHub (pinned to 807d45a6b3)