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
- Make the closing tag exactly the expected delimiter: `{% if %}...{% endif %}`, `{% for %}...{% endfor %}`, etc.
- Search the template for the opening tag named in the error and fix its matching closer.
- Check nesting order — an inner block may have swallowed the outer block's closer.
- 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
- Let the editor auto-insert closing tags when you type an opening block tag.
- Match open/close pairs innermost-first when hand-editing.
- Never rename an opening tag without its closer.
- Write a lint step that checks tag pairing.
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
- errors.syntax.unexpected_else (locale: Unexpected 'else'…
- errors.syntax.unknown_tag (locale: 'Unknown tag 'tag'')
- errors.syntax.tag_never_closed
- errors.syntax.tag_termination
- errors.syntax.variable_termination
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)