Shopify/liquid · error · Liquid::SyntaxError
errors.syntax.for
errors.syntax.for
Error message
options[:locale].t("errors.syntax.for") What it means
Liquid raises this SyntaxError from the {% for %} tag's lax_parse when the tag markup doesn't match the required 'for <var> in <collection>' shape. The lax parser scans the markup with TagAttributes and, on falling into the else branch, cannot find a valid variable/collection pair. It signals that the for-loop declaration itself is malformed (not the loop body).
Solutions
- Fix the for tag to the form {% for item in collection %} with a variable name and a collection expression
- If the collection may be empty/nil, ensure the expression is still syntactically present (e.g. {% for item in products %}), guarding emptiness inside the loop
- Run the template through Template.parse in a test to catch the SyntaxError at load time instead of render time
Example fix
// before
{% for %} ... {% endfor %}
// after
{% for item in products %} ... {% endfor %} Defensive patterns
Strategy: validation
Validate before calling
markup = tag_markup # the text between {% for and %}
valid = markup =~ /\A\s*[\w\-]+\s+in\s+.+\s*\z/
raise Liquid::SyntaxError, 'for tag must be: for <var> in <collection>' unless valid Type guard
def valid_for_tag?(markup) !!(markup.to_s =~ /\A\s*[\w\-\"']+\s+in\s+\S/) end
Try / catch
begin
Liquid::Template.parse(template)
rescue Liquid::SyntaxError => e
logger.error("Liquid for-tag syntax: #{e.message}")
fallback_template
end Prevention
- Always write for tags as {% for item in collection %}
- Parse all templates at deploy time (Template.parse) so syntax errors surface before render
- Lint templates in CI with theme-check or similar
When it happens
Trigger: A {% for %} tag whose markup lacks the 'in' structure, e.g. {% for %}, {% for item %}, or {% for in items %}; any for tag failing lax mode syntax parsing in parse.liquid templates rendered with the default (lax) parser.
Common situations: Hand-edited templates with a deleted variable or collection expression; template strings built dynamically via interpolation where a variable came out empty; copy-paste from docs that dropped the 'item in collection' part.
Related errors
- errors.syntax.unexpected_outer_tag
- errors.syntax.unknown_tag (locale: 'Unknown tag 'tag'')
- errors.syntax.assign
- errors.syntax.capture
- errors.syntax.case
AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08).
Data as JSON: /api/errors/61665208b6920df0.
Report an issue: GitHub.
Appendix: source
Thrown at lib/liquid/tags/for.rb:86
end
output
end
protected
def lax_parse(markup)
if markup =~ Syntax
@variable_name = Regexp.last_match(1)
collection_name = Regexp.last_match(2)
@reversed = !!Regexp.last_match(3)
@name = "#{@variable_name}-#{collection_name}"
@collection_name = parse_expression(collection_name)
markup.scan(TagAttributes) do |key, value|
set_attribute(key, value)
end
else
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")View on GitHub (pinned to 807d45a6b3)