Shopify/liquid · error · Liquid::SyntaxError

errors.syntax.for_invalid_attribute

errors.syntax.for_invalid_attribute

Error message

options[:locale].t("errors.syntax.for_invalid_attribute")

What it means

Raised by strict_parse for {% for %} when an attribute after the collection is neither 'limit' nor 'offset'. Strict mode only permits limit: and offset: parameters, each followed by a colon and an expression.

Solutions

  1. Replace unsupported keywords with limit: or offset:, e.g. {% for p in products limit: 5 offset: 2 %}
  2. Ensure a colon follows limit/offset: limit: 10 not limit 10
  3. Correct spelling of limit/offset attributes

Example fix

// before
{% for p in products by 2 %}...{% endfor %}
// after
{% for p in products limit: 5 offset: 2 %}...{% endfor %}
Defensive patterns

Strategy: validation

Validate before calling

attrs = markup.scan(/(\w+)\s*:/).flatten
bad = attrs - %w[limit offset reversed]
raise Liquid::SyntaxError, "invalid for attributes: #{bad.join(',')}" unless bad.empty?

Type guard

def valid_for_attributes?(markup)
  markup.to_s.scan(/(\w+)\s*:/).flatten.all? { |a| %w[limit offset].include?(a) }
end

Try / catch

begin
  Liquid::Template.parse(src, error_mode: :strict)
rescue Liquid::SyntaxError => e
  logger.error("Invalid for-tag attribute: #{e.message}")
  raise
end

Prevention

When it happens

Trigger: A for tag with a mistyped or unsupported parameter under the strict parser, e.g. {% for p in products by 2 %}, {% for p in products step: 2 %}, or {% for p in products limit 5 %} (missing colon).

Common situations: Porting templates from other template languages that use 'by'/'step' pagination keywords; typos like 'limt:' or 'offest:'; omitting the colon before the value in strict mode.

Related errors


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

Appendix: source

Thrown at lib/liquid/tags/for.rb:104

        raise SyntaxError, options[:locale].t("errors.syntax.for")
      end
    end

    def strict_parse(markup)
      p = @parse_context.new_parser(markup)
      @variable_name = p.consume(:id)
      raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')

      collection_name  = p.expression
      @collection_name = parse_expression(collection_name, safe: true)

      @name     = "#{@variable_name}-#{collection_name}"
      @reversed = p.id?('reversed')

      while p.look(:comma) || p.look(:id)
        p.consume?(:comma)
        unless (attribute = p.id?('limit') || p.id?('offset'))
          raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
        end
        p.consume(:colon)
        set_attribute(attribute, p.expression, safe: true)
      end
      p.consume(:end_of_string)
    end

    private

    def strict2_parse(markup)
      strict_parse(markup)
    end

    def collection_segment(context)
      offsets = context.registers[:for] ||= {}

      from = if @from == :continue
        offsets[@name].to_i

View on GitHub (pinned to 807d45a6b3)