---
title: Migrate to Shopify CLI 5.0
description: Update scripts and workflows for the breaking changes in Shopify CLI 5.0.
source_url:
  html: 'https://shopify.dev/docs/api/shopify-cli/migrate/5-0'
  md: 'https://shopify.dev/docs/api/shopify-cli/migrate/5-0.md'
api_name: shopify-cli
---

# Migrate to Shopify CLI 5.​0

Shopify CLI 5.0 changes JSON output contracts for theme commands, Shopify Functions, app configuration validation, channel configuration imports, store GraphQL execution, and subscription migrations. Update scripts, CI workflows, and integrations that consume these outputs. Function run and finite replay commands also buffer text output until execution completes.

Shopify CLI 5.0 also removes the `app logs` and `app logs sources` commands. To see Function logs from your dev preview, use `app dev`.

JSON contracts follow the CLI version. A breaking result change requires a major CLI version. Native content, such as GraphQL query data, Function runner output, TOML, and Speedscope profiles, retains its own field names and format.

***

## Requirements

* Check the [Shopify CLI requirements](https://shopify.dev/docs/api/shopify-cli#requirements).
* If you're upgrading from Shopify CLI 3.x, then complete the [4.0 migration](https://shopify.dev/docs/api/shopify-cli/migrate/4-0) first.
* Identify scripts that parse output from the affected commands.
* Save representative output from your current version, including empty results and failures, so you can compare your script's behavior after migration.

***

## Step 1: Install Shopify CLI 5.​0 in a test environment

Install Shopify CLI 5.0 in a test environment before updating your scripts or production automation. Shopify CLI doesn't upgrade automatically across major versions, so install 5.0 manually. For a global npm installation, use:

## terminal

```sh
npm install -g @shopify/cli@5
shopify version
```

For a project-local installation, update its `@shopify/cli` dependency and commit the lockfile.

Shopify CLI 4.x output doesn't match the 5.0 contracts. Update your scripts against the test installation, and release your script changes together with the CLI version change after verification.

***

## Step 2: Update JSON consumers

Pass `--json` explicitly when you need machine-readable output. Use `--no-input` separately when a command must run without prompting, and supply its required input flags.

Finite commands, which exit after they produce a result, write that result to stdout. Parse stdout as one JSON document. Progress and diagnostics use JSON events on stderr. Keep the streams separate instead of parsing combined stdout and stderr as a result. `app dev --json` streams JSON Lines (JSONL) instead, as described in Step 3.

### Collections and resource objects

Update array iteration and property lookups for named collections and resources:

| Command | Earlier output | 5.0 migration |
| - | - | - |
| `theme list` | Root array of themes | Read `themes`. Theme IDs are strings. |
| `app function info` | Function properties at the root | Read the `function` object and its `targets` array. |

An empty collection remains an array inside its object. For example, an empty theme library returns `{"themes": []}`.

The following examples show the `theme list` migration:

##### Before

```json
[
  {
    "id": 123,
    "name": "Example theme",
    "role": "unpublished",
    "processing": false,
    "createdAtRuntime": false
  }
]
```

##### 5.0

```json
{
  "themes": [
    {
      "id": "123",
      "name": "Example theme",
      "role": "unpublished",
      "processing": false
    }
  ]
}
```

### IDs, timestamps, and missing values

Update parsers and comparisons for the conventions used by the affected commands:

| Value | 5.0 contract | Required update |
| - | - | - |
| Theme IDs | Decimal strings in `id` and `developmentThemeId`. | Compare theme IDs as strings. |
| Subscription migration GIDs | `gid`, `shopGid`, `operationGid`, or `operationGids`, depending on the result. | Replace the earlier ID field names with the mappings in Step 3. |
| Subscription notification instants | Whole-second UTC timestamps, such as `2026-09-30T10:20:30Z`. | Remove dependencies on display dates, fractional seconds, or timezone offsets. |
| Subscription calendar dates | `YYYY-MM-DD`. | Parse these fields as calendar dates. Don't assume every date field includes a time. |
| Unavailable resource values | `null` where the command's schema specifies an unavailable value. | Handle `null` instead of treating an empty string as an unknown value. |
| Empty collections | `[]` under the named collection. | Handle an empty array without treating it as an error or missing result. |
| Filesystem paths | Absolute paths in the operating system's native format. | Use path utilities that support the platform where the command runs. |

CLI-owned field names use camelCase. Native content, such as GraphQL query aliases and Function runner `memory_usage`, keeps its original spelling.

***

## Step 3: Update affected commands

Each of the following sections covers one command group. Update the scripts that use those commands, and skip the sections for commands you don't use.

### Theme commands

Use string-valued IDs and the following field mappings:

| Command | Required update |
| - | - |
| `theme info` | Use `storeDomain`, `developmentThemeId`, `cliVersion`, and `nodeVersion`. When a theme is selected, read its `storeDomain`, `previewUrl`, and `editorUrl` inside `theme`. Handle unavailable values as `null`. |
| `theme list` | Iterate over `themes`. Stop reading the internal `createdAtRuntime` field. |
| `theme open` | Read URLs inside `theme.previewUrl` and `theme.editorUrl`, and use `theme.storeDomain`. `theme.src` becomes `theme.sourceUrl`, with `null` when unavailable. |
| `theme duplicate` | Read `status`, `changed`, `originalTheme`, and `theme`. Use `theme.storeDomain` and `theme.previewUrl`. Cancellation returns `status: "cancelled"`, and failure uses the fatal error document. |
| `theme push` | Read `status`, `changed`, `theme`, and `issues`. Replace the nested error map with `issues[].filePath` and `issues[].message`. Partial uploads return `status: "partial"` and exit nonzero while retaining completed work. |
| `theme preview` | Check `status: "success"`. Read the URL from `preview.url` and the opaque string identifier from `preview.id`, replacing the root `url` and `preview_identifier` fields. |
| `theme check` | Read `valid`, flat `issues`, `errorCount`, `warningCount`, and `infoCount`. Replace file-grouped `offenses` with `issues[].filePath`. Position fields use `startRow`, `startColumn`, `endRow`, and `endColumn`, with one-based lines and columns: the first line and column are `1`. Remove any `+1` conversion used to display these positions. The new report also applies to `--output json`. |
| `theme profile` | Preserve the native Speedscope profile. When `--environment` is explicit, read it from that environment's `result` in the batch object. |

Use the environment batch format in Step 4 whenever you explicitly pass `--environment` to these theme commands, including for one environment.

Remove `--poll` from `theme dev` invocations in your scripts. Shopify CLI 5.0 removes the flag. It has been deprecated since 3.61.0 and has had no effect since 3.70.0, so you don't need a replacement.

### Shopify Functions commands

For `app function info`, read `function.targets` as an array of records with `target`, `inputQueryPath`, and `export`. Replace lookups by target name in the former `targeting` object with a search by `target`. Use `function.apiVersion` and `function.schemaPath`, and handle unavailable values as `null`.

`app function run` preserves the native Function runner JSON fields, including `memory_usage`. A completed execution with `success: false` retains its result and exits nonzero. Setup failures, invalid runner output, and process failures use the fatal error document. Read diagnostics from stderr. Finite text output is buffered until execution completes.

Finite `app function replay --no-watch` text output also waits until replay completes. For finite JSON replay, pass `--no-watch`:

## terminal

```sh
shopify app function replay --no-watch --json
```

Don't combine the finite JSON result with replay's watch mode.

### App development sessions and JSON streaming

Use `app dev --json` to consume development output without parsing terminal text. For unattended runs, also pass `--no-input` and provide the configuration and store required for your session:

## terminal

```sh
shopify app dev --json --no-input
```

Parse stdout one line at a time. Each dev stream record has `timestamp`, `source`, and `message`, with `level` when available. Records include process output, preview URLs, and preview status messages. There isn't a final success result. Setup messages and diagnostics use stderr, and setup prompts still require input unless you pass `--no-input`.

Preview errors appear as log records while the session keeps running so you can fix your app. Fatal CLI failures write an `error` object to stdout and exit nonzero. Handle both log records and fatal errors when reading the stream.

Shopify CLI records each session's metadata and JSONL log under `.shopify/dev/`, including when you run without `--json`. In JSON mode, stdout contains the same dev stream records as the session log. These records contain text messages. They don't replace the structured Function log files in `.shopify/logs`.

Before starting another session for the same project and app configuration, reuse the running session or stop it. A second invocation exits with `error.code: "dev_instance_already_running"`. Read the existing session's metadata for its PID, URLs, `logFile`, and `details.preview` status. The preview status describes the CLI's state. It isn't an application health check.

Inspect the stream, fatal error, and session-file schemas with:

## terminal

```sh
shopify app dev --json-schema
```

### App configuration validation and channel imports

For `app config validate`, read diagnostic paths from `issues[].filePath` and `issues[].fieldPath` instead of `file` and `path`. `fieldPath` is an array of configuration keys and indexes, or `null`. `code` is `null` when unavailable. A completed validation returns its `valid` result even when validation fails. Setup failures return a fatal error document.

For `app import channel-config`, check `status: "success"` and use the absolute `path` of the written file. The native TOML stays in `toml`.

### Store Graph​QL execution

For `store execute --json`, read your query's result under `data`. Optional response metadata appears under `extensions`. Query field names and aliases inside `data` don't change.

For example, change a lookup of `products.nodes` to `data.products.nodes`:

##### Before

```json
{"products": {"nodes": []}}
```

##### 5.0

```json
{"data": {"products": {"nodes": []}}}
```

GraphQL failures use the fatal error document. Read upstream errors from `error.details.errors`, response metadata from `error.details.extensions`, and partial query data from `error.details.data` when available.

With `store execute --json --output-file`, the file contains the same `data` and optional `extensions` wrapper. Read the result from that file. Successful file output doesn't write a result or receipt to stdout.

### Subscription migrations

Update consumers of `app subscription-migrations list`, `status`, `schedule`, `unschedule`, and `cancel`:

| Earlier field or behavior | 5.0 migration |
| - | - |
| Operation `id` | Read `gid`. |
| References named `operationId` or `operationIds` | Read `operationGid` or `operationGids` in the corresponding result. |
| Subscription or result `shopId` | Read `shopGid`. |
| `results.edges[].node` | Iterate directly over `results`. |
| User error `field` | Read the array-valued `fieldPath`. |
| Schedule or unschedule fields under `submission` | Read `clientId`, `action`, `inputDigest`, `total`, and `operations` at the root, alongside `status` and `changed`. |
| Submission failure with completed batches | Handle `status: "partial"`, preserve `operations`, and inspect `failure`. |
| Declined schedule or unschedule confirmation | Handle `status: "cancelled"`, `changed: false`, and the cancellation reason, with exit code zero. |

Cancellation returns an overall `success` or `partial` status and an `operations` array. Each item has its own status and operation GID. Failed items retain their structured error details. Don't discard successful items when another cancellation fails.

For subscription dates, notification instants use whole-second UTC timestamps. `effectiveDate` retains a `YYYY-MM-DD` calendar date when that's what the source supplies. Upstream status and reason strings can gain new values, so handle values your integration doesn't recognize.

### App logs commands

Shopify CLI 5.0 removes `app logs` and `app logs sources`. Running either command returns an error and exits nonzero. To see Function logs, run [`shopify app dev`](https://shopify.dev/docs/api/shopify-cli/app/app-dev).

`app dev` only shows logs from your developer preview. It can't watch the released app version. To inspect Function runs from the released version, use [Function run logs in the Dev Dashboard](https://shopify.dev/docs/apps/build/functions/monitoring-and-errors#debug-a-function).

While `app dev` runs and your app has at least one Function extension, it streams Function run and network access logs from the dev store to your terminal. It also writes each log to a separate JSON file in your app's `.shopify/logs` directory, named `<timestamp>_<namespace>_<handle>_<id>.json`. Timestamps are in UTC, so sorting the filenames sorts the logs by time. [`app function replay`](https://shopify.dev/docs/api/shopify-cli/app/app-function-replay) reads the same files.

The following `ls` and `jq` examples read the log files that exist when you run them. They don't stream new logs. Run them again to include files written later.

| Earlier usage | 5.0 migration |
| - | - |
| `app logs` | Run `app dev`. Each log appears in the terminal with a link to its file. |
| `app logs sources` | Use the `handle` from each Function extension's `shopify.extension.toml`. Each source is named `extensions.<handle>`. |
| `--source extensions.<handle>` | List the matching files with `ls .shopify/logs/*_extensions_<handle>_*.json`, or filter with `jq -c 'select(.source == "<handle>")' .shopify/logs/*.json`. |
| `--status success` or `--status failure` | Filter on the top-level `status`, for example `jq -c 'select(.status == "failure")' .shopify/logs/*.json`. |
| `--json` | Use `app dev --json` for a stream of dev output messages. For structured Function records, print one JSON object per line with `jq -c . .shopify/logs/*.json`. Function run records use the same fields as the earlier `app logs --json` output. |
| Multiple `--store` flags | `app dev` connects to one store per session. Run it against each store in turn. Logs from every store go to the same directory, and each record includes `storeName`. |

In network access records, `payload` uses snake\_case keys instead of the camelCase keys in the earlier `app logs --json` output. For example, update `payload.httpRequest` lookups to `payload.http_request`.

`app dev` installs your local build on the store as the app's dev preview. This preview replaces the installed app version, or another developer's preview, until you run [`app dev clean`](https://shopify.dev/docs/api/shopify-cli/app/app-dev-clean). To leave a shared store unchanged, run `app dev` against your own dev store.

`app dev clean` doesn't remove `.shopify/logs`. Saved files can include logs from earlier sessions.

### Automation tokens

Rename `SHOPIFY_APP_AUTOMATION_TOKEN` or `SHOPIFY_CLI_PARTNERS_TOKEN` to `SHOPIFY_SERVICE_ACCOUNT_TOKEN` in your scripts and CI configuration, keeping the same token value. The old variables remain supported but are deprecated.

Unset both old variables before setting `SHOPIFY_SERVICE_ACCOUNT_TOKEN`. Shopify CLI rejects environments that set the new variable alongside either old variable.

***

## Step 4: Handle outcomes and failures

For finite commands, check the process exit code as well as the result. A nonzero exit can still include useful validation or partial-result data:

| Outcome | JSON behavior | Exit behavior |
| - | - | - |
| Completed success, no-op, skip, or declined confirmation | A result object, with `status` and `changed` where applicable. | Zero. |
| Validation that fails the selected blocking policy | A completed report with `valid: false` and issues. | Nonzero. |
| Partially completed requested work | A result retaining completed items and failure details. | Nonzero. |
| Failed single operation, setup, authentication, or transport | One fatal error document, such as `{"error": {"type": "abort", "message": "Authentication failed."}}`. | Nonzero. |

Fatal error types are `abort`, `bug`, and `external`. Read domain details under `error.details`. Use a stable `code` when the error supplies one, and handle its absence. Replace checks for legacy Boolean `error` values or root `status: "failed"` single-operation errors with checks for the error object.

For the affected theme commands, an explicit `--environment` returns one `environments` array in request order. Each item contains its `environment` and either `result` or `error`. Handle every item, including failures and cancellations. The command exits nonzero when any requested environment fails. For example:

## Environment batch

```json
{
  "environments": [
    {
      "environment": "staging",
      "result": {"themes": []}
    },
    {
      "environment": "production",
      "error": {"type": "abort", "message": "Authentication failed."}
    }
  ]
}
```

Without an explicit `--environment`, read the command's ordinary result object.

***

## Step 5: Verify your scripts and update production automation

Inspect each affected command's schema from the installed version. For example:

## terminal

```sh
shopify theme list --json-schema
```

For finite commands, the schema includes `Result`, `Error`, and `Event` definitions. Result and fatal error documents use stdout, while events use stderr. Match your parser to the installed command's result schema. For `app dev`, use the streaming and session-file schemas described in Step 3.

Run your scripts against the saved cases from your earlier version. Check successful and empty results, cancellations, validation failures, partial work, environment batches, and file output. Adding a new `--json` mode is separate from changing an existing JSON contract, so migrate the outputs your scripts already consume.

If your scripts fail after the upgrade, then reinstall the latest 4.x release and restore your 4.x-compatible scripts while you finish the migration. For a global npm installation, use `npm install -g @shopify/cli@4`.

After your scripts pass verification, update explicit CLI versions in CI workflows and release your script changes together with the CLI version change.

***

## Next steps

* Use the [Shopify CLI command reference](https://shopify.dev/docs/api/shopify-cli) to inspect command flags and schemas.
* Review [Shopify CLI releases](https://github.com/Shopify/cli/releases) for the contracts included in your installed version.

***
