I was redesigning the homepage of a client's Shopify store, reordering sections and adding a few new blocks. The routine was the usual one: edit templates/index.json, run shopify theme push, then open staging to check. The CLI came back with success. Line after line green, not a single warning. But the moment I refreshed staging, the page looked exactly as before. Not one of my changes had shown up.
My first instinct pointed entirely the wrong way. I assumed it was a cache issue, so I purged everything I could purge. No effect. Then I ran shopify theme pull to make sure my local was in sync, and what came back was baffling: the old content, not what I had just pushed. It was as if my push had been accepted, acknowledged, then silently discarded. At that point I started suspecting the theme was somehow "locked" — maybe someone else was editing through the theme editor, maybe Shopify had put a lock on its side. I spent hours chasing that lock theory. All of it wasted.
Why this happens
What finally cracked the puzzle was not a lock of any kind. It was a poorly documented limit: a Shopify JSON template's order array is hard-capped at 25 entries. Past 25, the push is rejected, but the default shopify theme push output still reports success with no error whatsoever. Staging silently keeps the last valid state. So from the outside everything looked like it ran cleanly, when in reality no bytes had actually moved.
How did I get past 25 in the first place? This is the sneaky part. During the redesign I did not always delete old sections — sometimes I just disabled them so I could switch them back on later. Those disabled sections stay preserved. Worse, there is an auto-restore behavior: if a section's definition still exists in the sections object, Shopify restores its id back into the order array on every push. So entries I thought I had removed kept reappearing. That accumulation of disabled-but-preserved sections plus the auto-restore pushed the count to 26+, and the moment it hit 26 the whole push became a no-op wearing a success banner.
This is a misleading pattern precisely because the "success" is a lie. If the CLI had said it failed, I would have read the error and been done in five minutes. What cost me an afternoon was the green banner over a push that quietly did nothing.
The fix
First clue: stop trusting the default output. Run the push with the --json flag to force the real error to surface:
shopify theme push --jsonWith --json, the error that had been hidden all along shows up immediately:
{ "errors": { "templates/index.json": ["order: must have a maximum of 25"] } }The moment I read that line, everything was clear. Not a lock, not a cache, not a broken theme. Just an overloaded order array.
The actual fix is to keep both the order array and the sections object at 25 or fewer. The key detail: deleting the id from order alone is not enough, because a section definition still living in sections re-seeds that id on the next push. So I genuinely pruned the disabled sections, removing their definition block from the sections object, not just their id from order:
shopify theme push --json
# read "order: must have a maximum of 25"
# delete the stale section definitions from the "sections" object, not just their "order" idsAs a guard so it never happens again, I check the array length before adding a new instance. If some tooling assembles the JSON programmatically:
if (data.order.length >= 25) {
throw new Error("order array is at the 25 cap; prune stale sections first");
}And I periodically audit the sections object to remove sections that have been disabled for a while, because they are the ones silently re-seeding order and pushing the count over the limit.
The takeaway
A "success" from the CLI is not proof your deploy actually landed. If shopify theme push reports success but staging stubbornly refuses to change, do not jump to a locked-theme theory — run shopify theme push --json first and read the raw error. The 25-entry cap on order is real and silent, and what keeps sneaking past it is disabled-but-preserved sections auto-restored from the sections object. Keep both order and sections at 25 or fewer, and prune the stale definitions, not just the ids. Since then, whenever a push is a little too quiet, I no longer accuse a lock — I ask the CLI to stop being polite and show me the error it is actually hiding.
