Version
liquid 5.12.0
- Ruby 4.0.5
- Default parse options, where
line_numbers is not enabled
Steps to reproduce
require "liquid"
Liquid::Template.parse("{% liquid liquid %}")
Actual behavior
Parsing raises a raw NoMethodError:
NoMethodError: undefined method '-' for nil
.../liquid-5.12.0/lib/liquid/block_body.rb:51:in `Liquid::BlockBody#parse_for_liquid_tag'
This happens in both :strict and :lax error modes.
Because the exception is not a Liquid::Error, application code that wraps parsing with rescue Liquid::Error will not catch it.
Expected behavior
The template should parse without crashing and render as an empty string.
This is already what happens when line numbers are enabled:
Liquid::Template.parse("{% liquid liquid %}", line_numbers: true).render!
# => ""
For comparison, the same template is accepted as a no-op by other major Liquid implementations:
- LiquidJS 10.27.0 renders
""
- python-liquid 2.2.1 renders
""
Root cause
The crash appears to come from lib/liquid/block_body.rb in parse_for_liquid_tag:
if tag_name == 'liquid'
parse_context.line_number -= 1
next parse_liquid_tag(markup, parse_context)
end
When line numbers are not tracked, parse_context.line_number is nil. A nested {% liquid %} line therefore executes:
which raises:
NoMethodError: undefined method '-' for nil
The nearby document-level parsing path already guards this kind of line number adjustment:
if parse_context.line_number
...
end
So the issue appears to be an asymmetry between the document-level path and the nested liquid tag path.
Trigger condition
This appears specific to a nested liquid line inside a {% liquid %} tag.
These controls do not trigger the same crash:
Liquid::Template.parse("{% liquid echo 1 %}")
Liquid::Template.parse("{% liquid %}")
Liquid::Template.parse("{% liquid render %}")
The render case fails cleanly as a Liquid parse error rather than escaping as a raw NoMethodError.
Version
liquid5.12.0line_numbersis not enabledSteps to reproduce
Actual behavior
Parsing raises a raw
NoMethodError:This happens in both
:strictand:laxerror modes.Because the exception is not a
Liquid::Error, application code that wraps parsing withrescue Liquid::Errorwill not catch it.Expected behavior
The template should parse without crashing and render as an empty string.
This is already what happens when line numbers are enabled:
For comparison, the same template is accepted as a no-op by other major Liquid implementations:
""""Root cause
The crash appears to come from
lib/liquid/block_body.rbinparse_for_liquid_tag:When line numbers are not tracked,
parse_context.line_numberisnil. A nested{% liquid %}line therefore executes:which raises:
The nearby document-level parsing path already guards this kind of line number adjustment:
So the issue appears to be an asymmetry between the document-level path and the nested
liquidtag path.Trigger condition
This appears specific to a nested
liquidline inside a{% liquid %}tag.These controls do not trigger the same crash:
The
rendercase fails cleanly as a Liquid parse error rather than escaping as a rawNoMethodError.