Shopify/liquid · error · Liquid::SyntaxError
Expected # but found #
Error message
Expected #{type} but found #{@tokens[@p].first} What it means
Liquid::Parser#consume raises Liquid::SyntaxError when the next token's type does not match the expected type (or when a specific type was requested but the token stream has something else). It signals malformed Liquid syntax during expression parsing, e.g. inside variables or variable_lookups.
Solutions
- Fix the Liquid template so the expected token is present (e.g. complete the identifier after the dot)
- Validate templates with Liquid::Template.parse in a test/CI step to catch syntax errors early
- Check for missing or extra dots, brackets, and pipes in the failing expression
Example fix
<!-- before -->
{{ product. }}
<!-- after -->
{{ product.title }} Defensive patterns
Strategy: validation
Validate before calling
def valid_liquid?(tpl) Liquid::Template.parse(tpl) true rescue Liquid::SyntaxError false end
Try / catch
begin
Liquid::Template.parse(template_source)
rescue Liquid::SyntaxError => e
logger.warn("Liquid syntax error: #{e.message}")
end Prevention
- Lint templates in CI with Liquid::Template.parse
- Avoid hand-editing expressions with dots/brackets without validation
- Render test fixtures for every template
When it happens
Trigger: A template expression like {{ product. }} or {{ a[ }} where an identifier, dot, or closing bracket is expected but a different token (or end of stream) is found; malformed range or argument syntax.
Common situations: Typo in a template (missing variable name after a dot); copy-pasted broken Liquid; dynamically generated templates with string interpolation errors; unclosed brackets.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- # is not a valid expression
- unsafe parse_expression cannot be used in strict2 mode
- Bare bracket access is not allowed. Use #
- Invalid expression type '#
- Memory limits exceeded
AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08).
Data as JSON: /api/errors/2b0890e5437a723c.
Report an issue: GitHub.
Appendix: source
Thrown at lib/liquid/parser.rb:19
# frozen_string_literal: true
module Liquid
class Parser
def initialize(input, reject_bare_brackets: false)
ss = input.is_a?(StringScanner) ? input : StringScanner.new(input)
@tokens = Lexer.tokenize(ss)
@p = 0 # pointer to current location
@reject_bare_brackets = reject_bare_brackets
end
def jump(point)
@p = point
end
def consume(type = nil)
token = @tokens[@p]
if type && token[0] != type
raise SyntaxError, "Expected #{type} but found #{@tokens[@p].first}"
end
@p += 1
token[1]
end
# Only consumes the token if it matches the type
# Returns the token's contents if it was consumed
# or false otherwise.
def consume?(type)
token = @tokens[@p]
return false unless token && token[0] == type
@p += 1
token[1]
end
# Like consume? Except for an :id token of a certain name
def id?(str)
token = @tokens[@p]View on GitHub (pinned to 807d45a6b3)