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
- Provide the full loop syntax: {% tablerow item in collection %}...{% endtablerow %}.
- Check that dynamic parts of the tag actually render (empty variable in template source).
- 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
- Never emit tablerow tags with interpolated empty segments
- Validate generated templates before saving
- Keep loop variable and collection present in the tag
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
- errors.syntax.for_invalid_in
- errors.syntax.unexpected_else (locale: Unexpected 'else'…
- errors.syntax.invalid_delimiter
- errors.syntax.unknown_tag (locale: 'Unknown tag 'tag'')
- errors.syntax.tag_never_closed
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'] = tablerowloopView on GitHub (pinned to 807d45a6b3)