Skip to content

Improve missing theme scope error message#7652

Open
charlespwd wants to merge 1 commit into
mainfrom
cp-friendly-theme-scope-error
Open

Improve missing theme scope error message#7652
charlespwd wants to merge 1 commit into
mainfrom
cp-friendly-theme-scope-error

Conversation

@charlespwd
Copy link
Copy Markdown
Contributor

@charlespwd charlespwd commented May 27, 2026

WHY are these changes introduced?

No public issue.

When theme commands use an account or token that cannot access Online Store themes, the Admin GraphQL API returns an ACCESS_DENIED error for the themes field. The CLI currently surfaces the raw GraphQL ClientError payload, which makes the actionable fix hard to find.

The high-volume observed case is a user-supplied theme password/token path, not a CLI-generated token path. Theme commands accept this value through --password, SHOPIFY_CLI_THEME_TOKEN, or a password entry in shopify.theme.toml. If that value is a custom app Admin API access token, the custom app owner/admin controls its scopes in Shopify Admin. When the custom app is missing read_themes, Shopify correctly denies the themes field.

If no password/token is supplied, the CLI uses the normal Shopify account OAuth flow and requests theme access. In that path, this error generally means the authenticated staff or collaborator account cannot access Online Store themes, so the fix is account permission/reauth rather than a missing custom app scope.

So the underlying failure is usually user/admin configuration. The CLI bug is that we present it like an unexpected GraphQL failure instead of an expected, actionable permission error.

WHAT is this pull request doing?

fetchThemes now detects Admin GraphQL ACCESS_DENIED errors that include a required access scope and rethrows them as a friendly AbortError.

Before, users saw a raw response/request JSON blob like:

Access denied for themes field. Required access: `read_themes` access scope.: {"response":{...},"request":{...}}

After, users see a direct explanation and next step:

The authenticated account or access token is missing `read_themes` access scope.

If you authenticated with a custom app Admin API access token, open the custom app in your Shopify admin, add the required theme access scopes, reinstall the app, and use the new access token. For theme pull, theme list, and theme info, add `read_themes`. For theme push and theme dev, add both `read_themes` and `write_themes`. If you authenticated with your Shopify account, make sure your staff or collaborator account can access Online Store themes, then run `shopify auth logout` and try again. See https://shopify.dev/api/usage/access-scopes.

How to test your changes?

  • pnpm --filter @shopify/cli-kit vitest run src/public/node/themes/api.test.ts

Post-release steps

No post-release steps.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — I've identified the correct bump type (patch for bug fixes · minor for new features · major for breaking changes) and added a changeset with pnpm changeset add

Fixes shop/issues#32995

@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label May 27, 2026
@charlespwd charlespwd force-pushed the cp-friendly-theme-scope-error branch from 010ba78 to fc8c6f2 Compare May 27, 2026 20:36
@charlespwd charlespwd marked this pull request as ready for review May 29, 2026 17:55
@charlespwd charlespwd requested review from a team as code owners May 29, 2026 17:55
Copilot AI review requested due to automatic review settings May 29, 2026 17:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves theme API error handling by converting missing theme access scope GraphQL failures into actionable CLI abort errors.

Changes:

  • Detects ACCESS_DENIED GraphQL errors with requiredAccess in fetchThemes.
  • Replaces the raw GraphQL client error with a friendlier AbortError and next-step guidance.
  • Adds test coverage and a patch changeset for the user-facing fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/cli-kit/src/public/node/themes/api.ts Adds missing theme scope detection and friendly error generation.
packages/cli-kit/src/public/node/themes/api.test.ts Covers the new friendly error path for missing read_themes.
.changeset/friendly-theme-scope-error.md Records the patch release note for @shopify/cli-kit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@charlespwd charlespwd force-pushed the cp-friendly-theme-scope-error branch from fc8c6f2 to 41b800b Compare May 29, 2026 17:58
Copy link
Copy Markdown
Contributor

@graygilmore graygilmore left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this! Couple of small things.

Comment on lines +621 to +622
'If you authenticated with a custom app Admin API access token, open the custom app in your Shopify admin,',
'add the required theme access scopes, reinstall the app, and use the new access token.',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the announcement about custom apps we might want to change the language here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a few conditionals in this new logic that aren't covered by the test. We don't need to add all (e.g. don't add a test asserting that the message doesn't appear for other exceptions) but some extra coverage would be good.

variables: {after},
responseOptions: {handleErrors: false},
preferredBehaviour: THEME_API_NETWORK_BEHAVIOUR,
}).catch((error: unknown) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we apply this same logic to findDevelopmentThemeByName? Are there any other theme fetches that we could drop this into?

const requiredAccess = accessDeniedError?.extensions?.requiredAccess
if (typeof requiredAccess !== 'string') return undefined

return requiredAccess.replace(/\.$/, '')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry that there's some fragility/assumptions here where we're relying on the shape of the translation from the API but I don't have a good alternative suggestion.

variables: {after},
responseOptions: {handleErrors: false},
preferredBehaviour: THEME_API_NETWORK_BEHAVIOUR,
}).catch((error: unknown) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(BOT COMMENT)

🎨 Code Style: Every other adminRequestDoc call in this file uses a bare await; the .catch() chained directly onto the awaited promise is the only instance of that pattern. A try/catch block reads more naturally for the intent (intercept an error, optionally translate it, otherwise rethrow) and doesn't rely on the reader verifying that abortIfMissingThemeAccessScope either throws or returns void.

Suggestion: Consider restructuring as:

let response
try {
  response = await adminRequestDoc({
    query: GetThemes,
    session,
    variables: {after},
    responseOptions: {handleErrors: false},
    preferredBehaviour: THEME_API_NETWORK_BEHAVIOUR,
  })
} catch (error) {
  abortIfMissingThemeAccessScope(error)
  throw error
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: @shopify/cli @shopify/cli package issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants