Shopify is removing the --force flag from shopify app deploy and shopify app release in a May 2026 CLI release. If your CI/CD pipeline calls either command with --force today, the first pipeline run after your next @shopify/cli bump will fail with "unknown flag."
The replacement flags exist now. They don't mean the same thing --force did. Swapping blindly either breaks deploys or — worse — silently starts deleting shop data that previously required confirmation.
What the failure looks like
CLI drops an unknown flag with a non-zero exit and a message like:
› Error: Nonexistent flag: --force
› See more help with --help
Which then cascades through whatever GitHub Actions / GitLab CI / CircleCI job wraps the command. Most pipelines surface this as a red build; some surface it as a silent skip if the step uses continue-on-error. Either way, no new app version ships until someone touches the pipeline.
The trigger isn't a calendar event — it's the CLI bump. If your pipeline pins @shopify/cli@3.x you're fine until your Renovate bot opens a PR. If your pipeline installs @shopify/cli latest on every run, it breaks the moment Shopify publishes the May 2026 release.
Why --force is going away
--force today bypasses every confirmation prompt — including "you are about to permanently delete an extension from every shop that has this app installed." That's a footgun in an interactive terminal. In CI, where nobody reads the prompt anyway, it's a data-loss hazard on someone else's store.
Shopify's fix is to split the single big-hammer flag into two narrower ones:
-
--allow-updates— permits adding and modifying extensions, still blocks deletions. -
--allow-deletes— permits deletions, intended only for manual runs.
--force was equivalent to --allow-updates --allow-deletes. The removal forces you to choose: does this pipeline actually need to delete extensions, or does it only update them?
For the vast majority of deploy pipelines, the answer is "only update." Use --allow-updates. Reserve --allow-deletes for manual operator runs where the person running the command has confirmed what's about to disappear.
Where --force is probably hiding
Search the pipeline files, not the source code:
git grep -n "force" -- '.github/**' '.gitlab-ci*' '*.yml' '*.yaml' 'package.json' 'scripts/**'
git grep -n "shopify app deploy\|shopify app release"
Common hiding spots:
-
GitHub Actions workflow steps —
run: shopify app deploy --forcein a step. -
Package.json scripts —
"deploy": "shopify app deploy --force --reset". -
Makefiles —
deploy: shopify app deploy --force. -
Shell wrappers —
scripts/deploy.sh,bin/release, any custom./deploythat shells out. -
Docs and runbooks — onboarding READMEs that tell new engineers to "just run
shopify app deploy --force."
The migration that actually works
For a standard CI/CD deploy pipeline:
Before:
- run: npx @shopify/cli@latest app deploy --force
After:
- run: npx @shopify/cli@latest app deploy --allow-updates
That covers the 90% case — merges to main ship extension updates, no deletions. If a PR deletes an extension and the pipeline needs to propagate the deletion, you have two options:
-
Gate deletion behind a manual step. A second workflow, triggered by a
workflow_dispatchor a specific tag, runningshopify app deploy --allow-updates --allow-deletes. -
Detect the deletion and promote the run. Parse the
shopify app deploy --dry-runoutput and branch: if the diff includes deletions, require an approver before re-running with--allow-deletes.
Avoid the temptation to just add --allow-deletes to every pipeline. That's the footgun --force was — the one Shopify is removing.
What the silent version of this failure looks like
The loud failure — "unknown flag" — is the easy case. You see it, you fix it.
The quiet failure is worse. Some pipelines add --allow-updates --allow-deletes as a global replacement for --force, thinking they've preserved the old behavior. They have — including the footgun. Six weeks later, a refactor PR removes an extension that was intentional. The deploy fires, the extension vanishes from every install, merchant support tickets spike. No alert triggered. The CLI did exactly what the pipeline asked.
"Match the old behavior" is the wrong migration strategy here. "Match the old behavior that we actually needed" is the right one, and for most teams that's just --allow-updates.
The pattern this fits
A CLI is an API. The flags, exit codes, and output format are the contract. Every pinned @shopify/cli version in your pipeline is an implicit dependency on a specific version of that contract. Bump the dependency, contract shifts, code written against the old contract breaks.
Shopify in the last six months:
- CLI
--forceflag removal (May 2026) - Checkout metafield deprecation (April 2026)
- Mandatory expiring tokens (April 2026)
- RBAC enforcement on admin APIs (April 2026)
- API version 2025-01 breaking changes (January 2026)
Every one of these breaks a different surface. Their common property is that none of them trigger anything on your side unless you're instrumenting the Shopify developer changelog or running integration tests against the live CLI and APIs on a schedule.
Minimum-viable fix
-
git grepevery pipeline file forshopify app deploy --forceandshopify app release --force. - Replace
--forcewith--allow-updatesfor automated pipelines. - Create a separate, manually-triggered workflow for deletions that uses
--allow-updates --allow-deletes. - Add
--dry-runto a pre-deploy step and fail the pipeline if the diff includes extension deletions the PR didn't explicitly flag. - Unpin
@shopify/cliin a canary job so the next breaking CLI change shows up in a non-production pipeline first. - Update internal runbooks and onboarding docs —
--forceis about to become a red herring.
The bigger point: your CI/CD depends on third-party CLIs and APIs whose contracts change faster than your review cadence. Either you monitor those contracts continuously, or you find out when the pipeline turns red on a Tuesday morning.
FlareCanary monitors REST APIs and MCP servers for schema drift. Free tier covers 5 endpoints with daily checks.
Top comments (0)