Shopify/liquid · error · Liquid::SyntaxError

errors.syntax.invalid_delimiter

Error message

errors.syntax.invalid_delimiter (locale: '{tag}' is not a valid delimiter for 'block_name' tags. use 'block_delimiter')

What it means

Liquid raises this SyntaxError when a block is closed with an `end...` tag that is not the delimiter the opening block expects — e.g. `{% tablerow %} ... {% endfor %}`. raise_unknown_tag checks tag.start_with?('end') and reports the actual tag, the block name, and the correct delimiter.

Solutions

  1. Make the closing tag exactly the expected delimiter: `{% if %}...{% endif %}`, `{% for %}...{% endfor %}`, etc.
  2. Search the template for the opening tag named in the error and fix its matching closer.
  3. Check nesting order — an inner block may have swallowed the outer block's closer.
  4. For custom blocks, ensure the delimiter registered with syntax matches the `end...` tag authors write.

Example fix

// before
{% capture greeting %}
  hello
{% endcomment %}
// after
{% capture greeting %}
  hello
{% endcapture %}
Defensive patterns

Strategy: validation

Validate before calling

BLOCK_DELIMS = { 'if'=>'endif', 'for'=>'endfor', 'capture'=>'endcapture', 'case'=>'endcase', 'unless'=>'endunless', 'raw'=>'endraw', 'tablerow'=>'endtablerow', 'comment'=>'endcomment' }
src.scan(/{%-?\s*(\w+)/).flatten.each_cons(2) { } # lint: pair each opener with correct end tag per BLOCK_DELIMS

Type guard

null

Try / catch

begin
  Liquid::Template.parse(src)
rescue Liquid::SyntaxError => e
  raise if e.message !~ /not a valid delimiter/
  logger.error("block delimiter mismatch: #{e.message}")
  fallback_template
end

Prevention

When it happens

Trigger: Closing `{% for %}` with `{% endfor2 %}`-style typos; mismatched pairs like `{% capture %}...{% endcomment %}`; closing a custom block with another block's end tag; `{% endraw %}` without `{% raw %}`.

Common situations: Hand-edited templates where an opening tag was renamed but the closing tag was not; nested blocks copied with wrong closers; custom tags whose delimiter string differs from the default (e.g. `end_<name>` mismatch).

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/2140410e3c3bd6be. Report an issue: GitHub.

Appendix: source

Thrown at lib/liquid/block.rb:44

    end

    def nodelist
      @body.nodelist
    end

    def unknown_tag(tag_name, _markup, _tokenizer)
      Block.raise_unknown_tag(tag_name, block_name, block_delimiter, parse_context)
    end

    # @api private
    def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
      if tag == 'else'
        raise SyntaxError, parse_context.locale.t(
          "errors.syntax.unexpected_else",
          block_name: block_name,
        )
      elsif tag.start_with?('end')
        raise SyntaxError, parse_context.locale.t(
          "errors.syntax.invalid_delimiter",
          tag: tag,
          block_name: block_name,
          block_delimiter: block_delimiter,
        )
      else
        raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
      end
    end

    def raise_tag_never_closed(block_name)
      raise SyntaxError, parse_context.locale.t("errors.syntax.tag_never_closed", block_name: block_name)
    end

    def block_name
      @tag_name
    end

View on GitHub (pinned to 807d45a6b3)