Shopify/liquid · error · Liquid::SyntaxError

errors.syntax.for_invalid_in

errors.syntax.for_invalid_in

Error message

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

What it means

The strict2 parser of the {% tablerow %} tag requires the literal keyword 'in' between the loop variable and the collection. If the parser cannot consume 'in' after the variable name, it raises Liquid::SyntaxError with the localized errors.syntax.for_invalid_in message.

Solutions

  1. Add the missing 'in' keyword: {% tablerow item in items %}...{% endtablerow %}.
  2. Fix typos in the keyword ('in' exactly, lowercase).
  3. Run Template.parse on the template in tests to catch the offending tag and line before rendering.

Example fix

// before
{% tablerow product products %}{{ product.title }}{% endtablerow %}
// after
{% tablerow product in products %}{{ product.title }}{% endtablerow %}
Defensive patterns

Strategy: validation

Validate before calling

raise 'tablerow requires: {% tablerow var in collection %}' unless markup =~ /\A\s*\w+\s+in\s+/

Type guard

def valid_tablerow_markup?(markup)
  markup =~ /\A\s*[a-zA-Z_]\w*\s+in\s+.+\z/
end

Try / catch

begin
  Liquid::Template.parse(source)
rescue Liquid::SyntaxError => e
  raise "tablerow markup error: #{e.message}"
end

Prevention

When it happens

Trigger: Markup like {% tablerow item items %} (missing 'in'), {% tablerow item in2 items %} (typo), or a parser in strict2 mode being fed a tablerow tag lacking the 'in' keyword.

Common situations: Hand-written templates missing 'in'; migrations from lenient parsers where malformed tags used to be tolerated; typos such as 'on' or 'inn'.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at lib/liquid/tags/table_row.rb:43

  # @liquid_optional_param range [untyped] A custom numeric range to iterate over.
  class TableRow < Block
    Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
    ALLOWED_ATTRIBUTES = ['cols', 'limit', 'offset', 'range'].freeze

    attr_reader :variable_name, :collection_name, :attributes

    def initialize(tag_name, markup, options)
      super
      parse_with_selected_parser(markup)
    end

    def strict2_parse(markup)
      p = @parse_context.new_parser(markup)

      @variable_name = p.consume(:id)

      unless p.id?("in")
        raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in")
      end

      @collection_name = safe_parse_expression(p)

      p.consume?(:comma)

      @attributes = {}
      while p.look(:id)
        key = p.consume
        unless ALLOWED_ATTRIBUTES.include?(key)
          raise SyntaxError, options[:locale].t("errors.syntax.table_row_invalid_attribute", attribute: key)
        end

        p.consume(:colon)
        @attributes[key] = safe_parse_expression(p)
        p.consume?(:comma)
      end

View on GitHub (pinned to 807d45a6b3)