Shopify/liquid · error · Liquid::SyntaxError

errors.syntax.table_row

errors.syntax.table_row

Error message

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

What it means

The lax parser of {% tablerow %} matches the markup against its syntax regexp; when there is no match (no variable/collection pair) it raises Liquid::SyntaxError with the localized errors.syntax.table_row message. It indicates the tablerow tag is fundamentally malformed.

Solutions

  1. Provide the full loop syntax: {% tablerow item in collection %}...{% endtablerow %}.
  2. Check that dynamic parts of the tag actually render (empty variable in template source).
  3. Validate templates with Template.parse in CI to catch malformed tags before runtime.

Example fix

// before
{% tablerow in %}{{ item }}{% endtablerow %}
// after
{% tablerow item in items %}{{ item }}{% endtablerow %}
Defensive patterns

Strategy: validation

Validate before calling

raise 'tablerow needs "var in collection"' unless markup =~ /\A\s*\w+\s+in\s+\S+/

Type guard

def complete_tablerow_tag?(markup)
  !markup.to_s.strip.empty? && markup.match?(/\A\s*\w+\s+in\s+/)
end

Try / catch

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

Prevention

When it happens

Trigger: {% tablerow %} with no arguments, {% tablerow in items %} missing the variable name, or any markup not matching the tag's SYNTAX regex, reached via strict_parse -> lax_parse.

Common situations: Truncated or half-edited templates; generated templates where interpolation of the collection name produced an empty string; copy-paste errors that dropped the loop variable.

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/29660d8f4d024075. Report an issue: GitHub.

Appendix: source

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

      end

      p.consume(:end_of_string)
    end

    def strict_parse(markup)
      lax_parse(markup)
    end

    def lax_parse(markup)
      if markup =~ Syntax
        @variable_name   = Regexp.last_match(1)
        @collection_name = parse_expression(Regexp.last_match(2))
        @attributes      = {}
        markup.scan(TagAttributes) do |key, value|
          @attributes[key] = parse_expression(value)
        end
      else
        raise SyntaxError, options[:locale].t("errors.syntax.table_row")
      end
    end

    def render_to_output_buffer(context, output)
      (collection = context.evaluate(@collection_name)) || (return '')

      from = @attributes.key?('offset') ? to_integer(context.evaluate(@attributes['offset'])) : 0
      to = @attributes.key?('limit') ? from + to_integer(context.evaluate(@attributes['limit'])) : nil

      collection = Utils.slice_collection(collection, from, to)
      length     = collection.length

      cols = @attributes.key?('cols') ? to_integer(context.evaluate(@attributes['cols'])) : length

      output << "<tr class=\"row1\">\n"
      context.stack do
        tablerowloop = Liquid::TablerowloopDrop.new(length, cols)
        context['tablerowloop'] = tablerowloop

View on GitHub (pinned to 807d45a6b3)