Shopify/liquid · error · Liquid::SyntaxError

# is not a valid expression

Error message

#{token} is not a valid expression

What it means

Liquid::Parser#expression raises Liquid::SyntaxError when the current token cannot start any recognized expression (identifier, string, number, range, etc.). The unexpected token text is embedded in the message.

Solutions

  1. Fix the template so a valid expression precedes operators/delimiters (e.g. add the missing value before | )
  2. Parse templates with Liquid::Template.parse in CI to catch this early
  3. Remove stray tokens (commas, pipes, colons) that aren't part of a complete expression

Example fix

<!-- before -->
{{ | upcase }}
<!-- after -->
{{ title | upcase }}
Defensive patterns

Strategy: validation

Validate before calling

def expression_starts_valid?(expr)
  expr !~ /^\s*[|,:.)]/
end

Try / catch

begin
  Liquid::Template.parse(tpl)
rescue Liquid::SyntaxError => e
  raise unless e.message =~ /is not a valid expression/
  # log template location and offending token
end

Prevention

When it happens

Trigger: Encountering tokens like an unexpected operator (|, ,, :), closing delimiter, or keyword where an operand is required, e.g. {{ | upcase }} or {% assign = 5 %}.

Common situations: Filters written without a preceding value; stray commas or colons in tag arguments; half-edited templates; malformed range literals like (..5).

Related errors


AI-assisted analysis of Shopify/liquid@807d45a6b3 (2026-09-08). Data as JSON: /api/errors/188ff7ed6936c19c. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/parser.rb:74

      when :open_square
        if @reject_bare_brackets
          raise SyntaxError, "Bare bracket access is not allowed. Use #{Expression::SELF}['...'] instead"
        end
        str = consume.dup
        str << expression
        str << consume(:close_square)
        str << variable_lookups
      when :string, :number
        consume
      when :open_round
        consume
        first = expression
        consume(:dotdot)
        last = expression
        consume(:close_round)
        "(#{first}..#{last})"
      else
        raise SyntaxError, "#{token} is not a valid expression"
      end
    end

    def argument
      str = +""
      # might be a keyword argument (identifier: expression)
      if look(:id) && look(:colon, 1)
        str << consume << consume << ' '
      end

      str << expression
      str
    end

    def variable_lookups
      str = +""
      loop do
        if look(:open_square)

View on GitHub (pinned to 807d45a6b3)