Shopify/liquid · error · Liquid::SyntaxError

Unexpected character #

Error message

Unexpected character #{ss.getch}

What it means

When the lexer's regex scanner cannot match any known token, raise_syntax_error rewinds to the start position and raises Liquid::SyntaxError reporting the unexpected character (using getch so multi-byte UTF-8 characters are reported whole). It means the template contains a character that cannot begin any valid Liquid token.

Solutions

  1. Locate the reported character in the template and fix or remove it (check for lone '{' or non-ASCII punctuation).
  2. Fix the delimiter to a valid one: {% ... %}, {{ ... }}, or a raw '{' that does not look like a broken tag start.
  3. Re-save the file as clean UTF-8 without smart quotes/control characters.
  4. Add a parse smoke test (Template.parse) in CI for templates edited by hand.

Example fix

// before
{ if x %}hello{{
// after
{% if x %}hello{% endif %}
Defensive patterns

Strategy: validation

Validate before calling

def no_lone_braces?(src)
  !src.match?(/\{(?![{%])/) # no '{' not followed by '{' or '%'
end

Try / catch

begin
  Liquid::Template.parse(src)
rescue Liquid::SyntaxError => e
  if (ch = e.message[/Unexpected character (.+)/, 1])
    report_syntax_location(src, ch)
  end
  raise
end

Prevention

When it happens

Trigger: Template.parse on source containing a stray character that breaks tokenization — e.g. an unmatched '{' not followed by '%' or '{', invalid control characters, or garbage bytes near tag delimiters.

Common situations: Hand-edited templates with a lone '{' or '}' mishandled; copy-paste introducing smart quotes or invisible control characters; templating scripts emitting malformed delimiters (e.g. '{ x }' or '{- if -}').

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at lib/liquid/lexer.rb:175

            else
              raise_syntax_error(start_pos, ss)
            end
          end
        end
        # rubocop:enable Metrics/BlockNesting
        output << EOS
      rescue ::ArgumentError => e
        if e.message == "invalid byte sequence in #{ss.string.encoding}"
          raise SyntaxError, "Invalid byte sequence in #{ss.string.encoding}"
        else
          raise
        end
      end

      def raise_syntax_error(start_pos, ss)
        ss.pos = start_pos
        # the character could be a UTF-8 character, use getch to get all the bytes
        raise SyntaxError, "Unexpected character #{ss.getch}"
      end
    end
  end
end

View on GitHub (pinned to 807d45a6b3)