Shopify/liquid · error · Liquid::InternalError
unsafe parse_expression cannot be used in strict2 mode
Error message
unsafe parse_expression cannot be used in strict2 mode
What it means
Liquid's parse_context raises Liquid::InternalError when ParseExpression is called with unsafe markup while the parser is in strict2 mode. In strict2, all expression markup must come from the parser itself (e.g. parser.expression) to guarantee the scanner state stays consistent. The safe flag lets API users opt in deliberately.
Solutions
- Extract expression markup via parser.expression (or parser or parser expression helpers) instead of parse_expression with raw strings
- Pass the safe flag (parse_expression with safe: true equivalent) after auditing that the markup comes from trusted parser output
- Disable strict2 mode if you must keep legacy behavior (not recommended)
Example fix
// before context.parse_expression(raw_markup) // after parser = Liquid::Parser.new(raw_markup) expr = context.parse_expression(parser.expression)
Defensive patterns
Strategy: try-catch
Validate before calling
raise Liquid::InternalError unless context.strict_parse_compatible?(markup) # ensure markup originates from parser.expression before calling parse_expression
Type guard
def parser_safe?(markup) markup.is_a?(String) && markup.frozen? # only parser-produced strings qualify in strict2 end
Try / catch
begin context.parse_expression(markup) rescue Liquid::InternalError => e # fall back to parser.expression extraction parser = Liquid::Parser.new(markup.to_s) context.parse_expression(parser.expression) end
Prevention
- Always source expression markup from parser.expression in custom tags
- Audit custom tags when enabling strict2 mode
- Add template-parsing tests covering every custom tag
When it happens
Trigger: Calling parse_expression (or ParseContext#parse_expression) with markup that was not produced by the parser while @strict_parse / strict2 mode is enabled, e.g. custom tags handing raw string markup into expression parsing.
Common situations: Custom tag implementations migrated to newer Shopify/Liquid versions with strict parsing enabled; embedding engines (like Shopify themes tooling) that force strict2; reusing legacy tag code that passed ad-hoc markup strings.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Expected # but found #
- Bare bracket access is not allowed. Use #
- # is not a valid expression
- undefined variable #
- Invalid expression type '#
AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08).
Data as JSON: /api/errors/850921d1f8ae97a9.
Report an issue: GitHub.
Appendix: source
Thrown at lib/liquid/parse_context.rb:66
line_number: start_line_number,
for_liquid_tag: for_liquid_tag,
)
end
def safe_parse_expression(parser)
Expression.safe_parse(parser, @string_scanner, @expression_cache)
end
def parse_expression(markup, safe: false)
if !safe && @error_mode == :strict2
# parse_expression is a widely used API. To maintain backward
# compatibility while raising awareness about strict2 parser standards,
# the safe flag supports API users make a deliberate decision.
#
# In strict2 mode, markup MUST come from a string returned by the parser
# (e.g., parser.expression). We're not calling the parser here to
# prevent redundant parser overhead.
raise Liquid::InternalError, "unsafe parse_expression cannot be used in strict2 mode"
end
Expression.parse(markup, @string_scanner, @expression_cache)
end
def partial=(value)
@partial = value
@options = value ? partial_options : @template_options
@error_mode = @options[:error_mode] || @environment.error_mode
end
def partial_options
@partial_options ||= begin
dont_pass = @template_options[:include_options_blacklist]
if dont_pass == true
{ locale: locale }
elsif dont_pass.is_a?(Array)View on GitHub (pinned to 807d45a6b3)