Shopify/liquid · error · Liquid::ArgumentError

invalid byte sequence in #

Error message

invalid byte sequence in #{result.encoding}

What it means

The url_decode Liquid filter raises Liquid::ArgumentError when CGI.unescape produces a string whose bytes are invalid in the result's encoding. This happens when percent-decoding yields bytes that don't form valid characters (e.g. invalid UTF-8 sequences).

Solutions

  1. Fix the input so percent escapes represent valid UTF-8 sequences (re-encode the source as UTF-8 before percent-encoding)
  2. Sanitize/strip invalid percent sequences from the input before applying the filter
  3. If binary data must round-trip, use base64 encoding instead of url encoding

Example fix

<!-- before -->
{{ '%FF' | url_decode }}
<!-- after -->
{{ '%C3%BF' | url_decode }}
Defensive patterns

Strategy: validation

Validate before calling

def url_decodable?(s)
  decoded = CGI.unescape(s.to_s)
  decoded.valid_encoding?
end

Type guard

def valid_encoding_string?(v)
  v.is_a?(String) && CGI.unescape(v).valid_encoding?
end

Try / catch

begin
  output = Liquid::Template.parse(tpl).render(assigns)
rescue Liquid::ArgumentError => e
  raise unless e.message.start_with?('invalid byte sequence')
  # sanitize input and retry or use fallback
end

Prevention

When it happens

Trigger: Calling {{ '%FF%FE' | url_decode }} or similar with percent escapes that decode to bytes invalid for the string encoding (commonly invalid UTF-8).

Common situations: Decoding URL-encoded binary data that isn't UTF-8; double-encoded or corrupted query strings from external systems; legacy percent-encodings of non-UTF-8 charsets (e.g. Latin-1 bytes).

Related errors


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

Appendix: source

Thrown at lib/liquid/standardfilters.rb:150

    # @liquid_syntax string | url_encode
    # @liquid_return [string]
    def url_encode(input)
      CGI.escape(Utils.to_s(input)) unless input.nil?
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Decodes any [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) characters
    #   in a string.
    # @liquid_syntax string | url_decode
    # @liquid_return [string]
    def url_decode(input)
      return if input.nil?

      result = CGI.unescape(Utils.to_s(input))
      raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?

      result
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string
    # @liquid_summary
    #   Encodes a string to [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
    # @liquid_syntax string | base64_encode
    # @liquid_return [string]
    def base64_encode(input)
      Base64.strict_encode64(Utils.to_s(input))
    end

    # @liquid_public_docs
    # @liquid_type filter
    # @liquid_category string

View on GitHub (pinned to 807d45a6b3)