I was working across two Shopify stores for two different client brands out of the same terminal. Bad habit, but a common one: one window, hopping between theme folders, pushing here and there. For one brand I needed to update a sections/product.liquid, so I ran what I thought was the right command, targeting the theme by its ID:
shopify theme push --theme=184148328826 --live --allow-liveThe success banner came back green. I moved on. Only a few minutes later did I notice something was off: the sections/product.liquid I had just pushed was nowhere to be found in the theme I expected. Worse, it had shown up on the other brand's LIVE production theme, as an orphan file that had nothing to do with the theme over there. I had just polluted the wrong store's production.
Why this happens
My first instinct was wrong: I assumed I had fat-fingered the ID. But the ID was correct — it really was a theme ID on store B. The problem was not the ID, it was which store the CLI was pointed at.
The Shopify CLI holds an authenticated store context, and that context is sticky between commands. An earlier command in that terminal had left me authenticated to store A. When I ran the push with --theme=184148328826, that ID was a theme that only exists on store B, not A. What I assumed would happen: the CLI resolves the ID, realizes it is not one of store A's themes, and errors out. What actually happened: the CLI failed to resolve that ID on store A, then silently fell back to store A's live theme instead of refusing.
So --theme, which I had treated as an absolute pointer to a specific theme, is really only interpreted within whatever store is currently active. If the ID does not match the active store, it does not shout. It guesses — and its guess is the live theme. That is how the orphan file landed on the wrong brand's production.
The recovery
Since that file genuinely did not belong to the target theme, I could not simply "undo" the push. I pushed a blank stub file to the same path to neutralize the orphan, so the stray sections/product.liquid no longer rendered anything and was referenced by no template. Ugly, but it stopped the bleeding. Then I cleaned up properly.
The fix
The core lesson is simple: never trust the implicit store context in a multi-store workspace. Pass --store explicitly on every remote command — push, pull, dev, and console alike:
shopify theme push --store=example-store.myshopify.com --theme=184148328826 --live --allow-liveWith an explicit --store, the CLI stops guessing. If that theme ID does not exist on the store I named, it errors out instead of falling back to live. That is exactly the behavior I want.
The second habit I now enforce: before any push, I confirm that the theme ID actually resolves on the store I mean. One quick command does it:
shopify theme list --store=example-store.myshopify.comIf the target ID appears in that list, I am safe. If it does not, then I am pointing at the wrong store and I would rather learn it now, before touching production.
And finally, as trivial as it looks: read the store URL in the CLI success banner before trusting it. The banner tells you which store actually received the push. I used to skip that line because it was "obviously right." It was not.
The takeaway
--theme=<ID> is not an absolute address. It only means something within the currently active store, and that context is sticky from a prior command. If the ID does not match the active store, the Shopify CLI does not error — it silently falls back to the active store's live theme, which can be an entirely different brand's production. In a multi-store workspace, treat --store=<store>.myshopify.com as mandatory on every remote command, pre-check with shopify theme list --store=<expected> so the target ID is proven to resolve, and read the store URL in the success banner before trusting it. Since that incident, I have never run a remote Shopify command without an explicit --store. Guessing the wrong store is cheap for the CLI, but expensive for the client.
