diff --git a/packages/app/src/cli/services/dev/fetch.test.ts b/packages/app/src/cli/services/dev/fetch.test.ts index 73e9c40cef..02447fb71e 100644 --- a/packages/app/src/cli/services/dev/fetch.test.ts +++ b/packages/app/src/cli/services/dev/fetch.test.ts @@ -11,7 +11,6 @@ import {AppManagementClient} from '../../utilities/developer-platform-client/app import {afterEach, describe, expect, test, vi} from 'vitest' import {renderFatalError} from '@shopify/cli-kit/node/ui' import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' -import {AbortError} from '@shopify/cli-kit/node/error' const ORG1: Organization = { id: '1', @@ -121,7 +120,7 @@ describe('fetchStore', () => { const got = fetchStore(ORG1, 'domain1', developerPlatformClient) // Then - await expect(got).rejects.toThrow(new AbortError(`Could not find Store for domain domain1 in Organization org1.`)) + await expect(got).rejects.toThrow(`Your current account (org1) doesn't have access to store domain1.`) }) }) diff --git a/packages/app/src/cli/services/dev/fetch.ts b/packages/app/src/cli/services/dev/fetch.ts index 94d8bd436e..a8b2740cc5 100644 --- a/packages/app/src/cli/services/dev/fetch.ts +++ b/packages/app/src/cli/services/dev/fetch.ts @@ -125,7 +125,16 @@ export async function fetchStore( ): Promise { const store = await developerPlatformClient.storeByDomain(org.id, storeFqdn) - if (!store) throw new AbortError(`Could not find Store for domain ${storeFqdn} in Organization ${org.businessName}.`) + if (!store) { + throw new AbortError( + `Your current account (${org.businessName}) doesn't have access to store ${storeFqdn}.`, + undefined, + [ + ['Select a different store with', {command: '--store'}, 'or', {command: '--reset'}], + ['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'], + ], + ) + } return store } diff --git a/packages/cli-kit/src/private/node/session/exchange.ts b/packages/cli-kit/src/private/node/session/exchange.ts index 1fcb3024ee..412b29273c 100644 --- a/packages/cli-kit/src/private/node/session/exchange.ts +++ b/packages/cli-kit/src/private/node/session/exchange.ts @@ -212,6 +212,8 @@ function tokenRequestErrorHandler({error, store}: {error: string; store?: string } if (error === 'invalid_target') { return new InvalidTargetError(invalidTargetErrorMessage, '', [ + ['Select a different store with', {command: '--store'}], + ['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'], 'Ensure you have logged in to the store using the Shopify admin at least once.', 'Ensure you are the store owner, or have a staff account if you are attempting to log in to a dev store.', 'Ensure you are using the permanent store domain, not a vanity domain.', diff --git a/packages/cli-kit/src/public/node/api/admin.test.ts b/packages/cli-kit/src/public/node/api/admin.test.ts index f63268f667..1e259226b1 100644 --- a/packages/cli-kit/src/public/node/api/admin.test.ts +++ b/packages/cli-kit/src/public/node/api/admin.test.ts @@ -5,6 +5,7 @@ import {buildHeaders} from '../../../private/node/api/headers.js' import * as http from '../../../public/node/http.js' import {defaultThemeKitAccessDomain} from '../../../private/node/constants.js' import {test, vi, expect, describe} from 'vitest' +import {ClientError} from 'graphql-request' vi.mock('./graphql.js') vi.mock('../../../private/node/api/headers.js') @@ -90,6 +91,19 @@ describe('admin-graphql-api', () => { variables: {variables: 'variables'}, }) }) + + test('throws helpful error when admin API returns 403', async () => { + // Given + const errorResponse = { + status: 403, + errors: [], + headers: new Headers(), + } + vi.mocked(graphqlRequestDoc).mockRejectedValue(new ClientError(errorResponse, {query: ''})) + + // When/Then + await expect(admin.supportedApiVersions(Session)).rejects.toThrow(/don't have access to this store/) + }) }) describe('admin-rest-api', () => { diff --git a/packages/cli-kit/src/public/node/api/admin.ts b/packages/cli-kit/src/public/node/api/admin.ts index f10e092ce1..c98a2ceb81 100644 --- a/packages/cli-kit/src/public/node/api/admin.ts +++ b/packages/cli-kit/src/public/node/api/admin.ts @@ -170,13 +170,17 @@ async function fetchApiVersions(session: AdminSession, preferredBehaviour?: Requ return response.publicApiVersions } catch (error) { if (error instanceof ClientError && error.response.status === 403) { - const storeName = session.storeFqdn.replace('.myshopify.com', '') throw new AbortError( - outputContent`Looks like you don't have access this dev store: (${outputToken.link( - storeName, + outputContent`Looks like you don't have access to this store: ${outputToken.link( + session.storeFqdn, `https://${session.storeFqdn}`, - )})`, - outputContent`If you're not the owner, create a dev store staff account for yourself`, + )}`, + undefined, + [ + ['Select a different store with', {command: '--store'}], + ['Use', {command: 'shopify auth login'}, 'to log in with a different account that has access to this store'], + "If you're not the owner, create a dev store staff account for yourself", + ], ) } if (error instanceof ClientError && (error.response.status === 401 || error.response.status === 404)) {