What does "'if' tag was never closed" mean in Shopify?
It means a Liquid tag in that file was opened and never ended: an {% if %} with no {% endif %}, a {% for %} with no {% endfor %}, and so on. Shopify refuses to save the whole file, so the push or upload shows Liquid syntax error (line 4): 'if' tag was never closed and the store keeps the previous version of that file.
The fix is to add the missing end tag, but do not trust the line number to find it: in our test it pointed at the last line of the file, not at the tag that was left open. Below is every message Shopify printed for 26 broken files we pushed to a development store on 29 October 2026, and the 8 that shopify theme check did not flag.
Which end tag am I missing?
The word in quotes is the tag that was opened. Each one needs its own end tag, and the pair has to match exactly:
| The message says | Add this end tag | Where the line number pointed in our test |
|---|---|---|
'if' tag was never closed | {% endif %} | The end of the file |
'for' tag was never closed | {% endfor %} | The end of the file |
'unless' tag was never closed | {% endunless %} | The end of the file |
'case' tag was never closed | {% endcase %} | The end of the file |
'capture' tag was never closed | {% endcapture %} | The end of the file |
'form' tag was never closed | {% endform %} | The end of the file |
'style' tag was never closed | {% endstyle %} | The end of the file |
'comment' tag was never closed | {% endcomment %} | The line where the tag opened |
'raw' tag was never closed | {% endraw %} | The line where the tag opened |
'javascript' tag was never closed | {% endjavascript %} | The line where the tag opened |
'schema' tag was never closed | {% endschema %} | The line where the tag opened |
'doc' tag was never closed | {% enddoc %} | The line where the tag opened |
The split is the useful part. For if, for, unless, case, capture, form and style, Shopify only notices the problem when it reaches the end of the file, so the line number is the last line. With the open if on line 2 of a 3 line file, the message said line 4, the empty line after the last one. For comment, raw, javascript, schema and doc, which swallow everything after them, the line number was the line where the tag started, so you can go straight to it.
How do I find the tag that was left open?
Count the pairs. Open the file named in the message (in Online Store, Themes, the three dots, Edit code), search for if tags and endif tags, and compare the counts; the one with fewer end tags is the one to fix. Three things make the count come out wrong:
- Whitespace dashes.
{%- if -%}is the same tag as{% if %}, so count both spellings:{% ifand{%- if, then{% endifand{%- endif. {% liquid %}blocks. Inside one, tags have no brackets at all: a bare line readingif product.availableneeds a bareendifline. In our test a missing one inside aliquidblock gave'if' tag was never closedon the line of the block's closing%}.- Nesting. An
ifinside anotherifthat has only oneendifgave the same message as a single missing one. Shopify does not say which of the two is open. Liquid pairs eachendifwith the nearest openif, so to Shopify it is the outer one, but the end tag you forgot may belong to either.
Work through the file top to bottom and indent each block as you go. The open tag is the one whose indent never comes back.
What do the other Liquid syntax errors mean?
The same 26 file test produced these messages for closing tags in the wrong place, typos and missing brackets:
| What Shopify printed | What was wrong in the file | Fix |
|---|---|---|
Unknown tag 'endif' | An {% endif %} with no if before it, usually one end tag too many after an edit | Delete the extra endif |
Unexpected outer 'else' tag | An {% else %} outside any if | Move it inside the if it belongs to, or delete it |
'endfor' is not a valid delimiter for if tags. use endif | An if closed with the wrong end tag | Change it to {% endif %} |
'endif' is not a valid delimiter for for tags. use endfor | Crossed nesting: {% if %}{% for %}...{% endif %}{% endfor %} | Close the inner tag first: endfor, then endif |
'endunless' is not a valid delimiter for if tags. use endif | An if closed with endunless | Change it to {% endif %} |
'end' is not a valid delimiter for if tags. use endif | {% end if %}, with a space | Write it as one word: {% endif %} |
Unknown tag 'elseif' | elseif, the spelling from PHP and other languages | Liquid spells it {% elsif %} |
Variable '{{ shop.name }' was not properly terminated with regexp: /\}\}/ | An output tag closed with one brace, or not closed at all | End it with two braces: }} |
Unexpected character / in "true <p>x</p>..." | {% if true with its %} missing. Liquid read on into the HTML until it found the next %} | Add the %}. The text in quotes shows where the tag should have ended |
Unexpected character " in "shop.name == "x" | A quote opened inside a condition and never closed | Close the quote |
The regexp: /\}\}/ part is not something to fix; it is Liquid saying it was looking for }}. And when the variable is not closed at all, the quoted text in the message runs on into whatever came next in the file, in our test {{ shop.name </h1> followed by the next line, which is a quick way to spot where it starts.
Why did my change not show up on the store?
Because Shopify keeps the last version of the file that was valid. We pushed a working section that printed VERSION-ONE, then pushed an edit of it with a missing endif that would have printed VERSION-TWO. The push reported 'if' tag was never closed for that file, and when we pulled the file back from Shopify and loaded the storefront, both still had the first version. Nothing on the store broke, but nothing changed either, so a missed error message reads as "my edit did nothing".
The rest of the push still goes through. shopify theme push uploaded every valid file, finished with exit code 0 and a warning that the theme was pushed with errors, and listed the rejected files. If you run pushes from a script or a GitHub workflow, check the output for that warning, because the exit code will not tell you. For a ZIP upload, where the same errors appear as a list after the theme fails, see why a Shopify theme ZIP will not upload.
Why does my template also say "does not refer to an existing section file"?
Because the broken section was never saved, so a template that asks for it points at nothing. Our homepage template used two of the broken sections, and Shopify refused it with Section type 'c08-stray-endif' does not refer to an existing section file. It named only one of the two missing sections, so expect the message to change to the next one after you fix the first. Fix the Liquid syntax errors and this one goes away by itself, the same chain we saw with "default can't be blank" schema errors.
Why didn't theme check catch it?
It caught most of them, in different words, and missed 8. We ran shopify theme check (Shopify CLI 3.93.1) on the same 26 files before pushing:
| Broken file | theme check | Shopify on push |
|---|---|---|
Missing endif, endfor, endunless, endcase, endcapture or endform | Flagged, as Attempting to end parsing before LiquidBranch 'null' was closed or a similar line naming 'when', 'capture' or 'form' | Rejected: 'if' tag was never closed and so on |
Stray endif | Flagged: Attempting to close LiquidTag 'if' before it was opened | Rejected: Unknown tag 'endif' |
| Crossed or wrong end tag | Flagged: Attempting to close LiquidTag 'for' before LiquidTag 'if' was closed | Rejected: is not a valid delimiter |
| Output tag with one brace or none | Flagged: SyntaxError: expected "}}" | Rejected: was not properly terminated |
Missing endcomment, endraw, endstyle, endjavascript, endschema or enddoc | Not flagged | Rejected: tag was never closed |
elseif instead of elsif | Not flagged | Rejected: Unknown tag 'elseif' |
Unclosed quote inside an if | Not flagged | Rejected: Unexpected character " |
So a clean theme check does not mean the push will pass. The missed group includes {% endschema %}, a tag nearly every section has. The only complete check is Shopify itself, so push to an unpublished theme first (shopify theme push --unpublished) and read the list of rejected files before you publish anything.
Where these results come from
Every message above comes from one test theme with 26 deliberately broken files (25 sections and 1 snippet), pushed to a Shopify development store as an unpublished theme on 29 October 2026 with Shopify CLI 3.93.1, plus a second push of a working section followed by a broken edit of it. All 26 files were rejected, and the theme was deleted afterwards. We did not test the online code editor's Save button. For errors that appear on the storefront rather than at upload, see what "invalid url input" means, and if you are editing a theme you plan to update later, read whether you will lose your customizations first.
Frequently asked questions
What does "'if' tag was never closed" mean in Shopify?
An if tag in that file has no matching endif. Shopify treats it as a Liquid syntax error and refuses to save the whole file, so the push or upload lists it as rejected and the store keeps the previous version of the file. Add the missing {% endif %} and push again.
Why is the line number wrong in a "tag was never closed" error?
For if, for, unless, case, capture, form and style, Shopify only notices at the end of the file, so the line number is the last line, not the open tag. In our 29 October 2026 test a 3 line file with the open if on line 2 reported line 4. For comment, raw, javascript, schema and doc, the number was the line where the tag opened.
What does "Unknown tag 'endif'" mean?
There is an {% endif %} with no if before it, usually one end tag too many left behind after editing. Delete the extra one. Shopify rejected the whole file for it in our test, the same as a missing endif.
What does "'endfor' is not a valid delimiter for if tags. use endif" mean?
An if was closed with the wrong end tag, or two blocks cross, such as {% if %}{% for %} closed with endif before endfor. Close the inner tag first and match each end tag to its opening tag.
What does "Variable was not properly terminated with regexp" mean in Liquid?
An output tag such as {{ product.title }} is missing its closing }}, or was closed with a single }. The regexp part is Liquid saying it was looking for two closing braces. The quoted text in the message shows where the unfinished tag begins.
Is it elsif or elseif in Shopify Liquid?
elsif. Shopify rejected a file using elseif with "Unknown tag 'elseif'" in our test, and shopify theme check did not flag it, so it is easy to miss until the push.
Why didn't my edit show up after the push?
Because the edited file was rejected and Shopify kept the last valid version. In our test a section with a missing endif was refused, and both the stored file and the storefront still showed the old version. The push itself still exits with code 0 and only warns that the theme was pushed with errors.
Does shopify theme check catch "tag was never closed"?
Mostly, but not always. In our test theme check flagged 18 of 26 broken files and missed 8, including a missing endschema, endcomment, endstyle or endjavascript, the elseif typo, and an unclosed quote. Shopify rejected all 26. Push to an unpublished theme to see the full list.
Generate a theme that looks like your brand
A complete Shopify 2.0 theme with conversion features built in, ready in minutes. No credit card required.
Generate my theme freeNo Shopify store yet? Start one here, then bring the theme. Themr may earn a commission if you start a paid plan; it does not change what you pay.