diff --git a/.changeset/cli-error-copy.md b/.changeset/cli-error-copy.md new file mode 100644 index 00000000000..de6835d2291 --- /dev/null +++ b/.changeset/cli-error-copy.md @@ -0,0 +1,5 @@ +--- +'@clerk/shared': patch +--- + +Update missing and invalid key error messages to recommend the Clerk CLI: `npx clerk@latest init` (non-interactive, no Clerk account required) to create an application, `npx clerk@latest env pull` to fetch the keys of an existing one, and `npx clerk@latest deploy` / `npx clerk@latest env pull --instance prod` for production. This covers both the `errorThrower` messages and the errors thrown by `parsePublishableKey(key, { fatal: true })`, which previously surfaced server-side as a bare `Publishable key not valid.` The Dashboard link is kept for manual key copying. diff --git a/packages/backend/src/__tests__/createRedirect.test.ts b/packages/backend/src/__tests__/createRedirect.test.ts index 0877146bb89..3b31090e688 100644 --- a/packages/backend/src/__tests__/createRedirect.test.ts +++ b/packages/backend/src/__tests__/createRedirect.test.ts @@ -28,7 +28,7 @@ describe('redirect(redirectAdapter)', () => { } as any); expect(() => redirectToSignIn({ returnBackUrl })).toThrowError( - '@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.', + '@clerk/backend: Missing publishableKey. To set up Clerk for this project, in your terminal run:\n\n npx clerk@latest init', ); }); }); @@ -258,7 +258,7 @@ describe('redirect(redirectAdapter)', () => { }); expect(() => redirectToSignUp({ returnBackUrl })).toThrowError( - '@clerk/backend: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.', + '@clerk/backend: Missing publishableKey. To set up Clerk for this project, in your terminal run:\n\n npx clerk@latest init', ); }); diff --git a/packages/shared/src/__tests__/error.spec.ts b/packages/shared/src/__tests__/error.spec.ts index 47981be4a4e..3e454703294 100644 --- a/packages/shared/src/__tests__/error.spec.ts +++ b/packages/shared/src/__tests__/error.spec.ts @@ -16,13 +16,13 @@ describe('ErrorThrower', () => { it('throws the correct error message and interpolates pkg and known parameters', () => { expect(() => errorThrower.throwInvalidPublishableKeyError({ key: 'whatever' })).toThrow( - '@clerk/test-package: The publishableKey passed to Clerk is invalid. You can get your Publishable key at https://dashboard.clerk.com/last-active?path=api-keys. (key=whatever)', + '@clerk/test-package: The publishableKey passed to Clerk is invalid (key=whatever, expected format: pk_test_... or pk_live_...). To create a Clerk application with valid keys, in your terminal run:\n\n npx clerk@latest init', ); }); it('throws the correct error message and interpolates pkg if no parameters are provided', () => { expect(() => errorThrower.throwMissingPublishableKeyError()).toThrow( - '@clerk/test-package: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.', + '@clerk/test-package: Missing publishableKey. To set up Clerk for this project, in your terminal run:\n\n npx clerk@latest init', ); }); diff --git a/packages/shared/src/__tests__/keys.spec.ts b/packages/shared/src/__tests__/keys.spec.ts index c932cacf404..5f7578b0843 100644 --- a/packages/shared/src/__tests__/keys.spec.ts +++ b/packages/shared/src/__tests__/keys.spec.ts @@ -60,7 +60,7 @@ describe('parsePublishableKey(key)', () => { it('throws an error for keys with extra characters after $ when fatal: true', () => { expect(() => parsePublishableKey('pk_live_ZmFrZS1jbGVyay1tYWxmb3JtZWQuY2xlcmsuYWNjb3VudHMuZGV2JGV4dHJh', { fatal: true }), - ).toThrowError('Publishable key not valid.'); + ).toThrowError('Publishable key not valid'); }); it('returns null for keys with multiple $ characters', () => { @@ -72,18 +72,16 @@ describe('parsePublishableKey(key)', () => { }); it('throws an error if the key cannot be decoded when fatal: true', () => { - expect(() => parsePublishableKey('pk_live_invalid!@#$', { fatal: true })).toThrowError( - 'Publishable key not valid.', - ); + expect(() => parsePublishableKey('pk_live_invalid!@#$', { fatal: true })).toThrowError('Publishable key not valid'); }); it('throws an error if the key is not a valid publishable key, when fatal: true', () => { - expect(() => parsePublishableKey('fake_pk', { fatal: true })).toThrowError('Publishable key not valid.'); + expect(() => parsePublishableKey('fake_pk', { fatal: true })).toThrowError('Publishable key not valid'); }); it('throws an error if the publishable key is missing, when fatal: true', () => { expect(() => parsePublishableKey(undefined, { fatal: true })).toThrowError( - 'Publishable key is missing. Ensure that your publishable key is correctly configured. Double-check your environment configuration for your keys, or access them here: https://dashboard.clerk.com/last-active?path=api-keys', + 'Publishable key is missing. To create a Clerk application with valid keys, in your terminal run:\n\n npx clerk@latest init', ); }); diff --git a/packages/shared/src/__tests__/loadClerkJsScript.spec.ts b/packages/shared/src/__tests__/loadClerkJsScript.spec.ts index 81191d47072..16b3b412daf 100644 --- a/packages/shared/src/__tests__/loadClerkJsScript.spec.ts +++ b/packages/shared/src/__tests__/loadClerkJsScript.spec.ts @@ -46,7 +46,7 @@ describe('loadClerkJsScript(options)', () => { test('throws error when publishableKey is missing', async () => { await expect(loadClerkJsScript({} as any)).rejects.toThrow( - '@clerk/react: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.', + '@clerk/react: Missing publishableKey. To set up Clerk for this project, in your terminal run:\n\n npx clerk@latest init', ); }); @@ -310,7 +310,7 @@ describe('loadClerkUIScript(options)', () => { test('throws error when publishableKey is missing', async () => { await expect(loadClerkUIScript({} as any)).rejects.toThrow( - '@clerk/react: Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.', + '@clerk/react: Missing publishableKey. To set up Clerk for this project, in your terminal run:\n\n npx clerk@latest init', ); }); diff --git a/packages/shared/src/errors/errorThrower.ts b/packages/shared/src/errors/errorThrower.ts index 030e1b68948..ccf46dccb66 100644 --- a/packages/shared/src/errors/errorThrower.ts +++ b/packages/shared/src/errors/errorThrower.ts @@ -1,8 +1,26 @@ const DefaultMessages = Object.freeze({ InvalidProxyUrlErrorMessage: `The proxyUrl passed to Clerk is invalid. The expected value for proxyUrl is an absolute URL or a relative path with a leading '/'. (key={{url}})`, - InvalidPublishableKeyErrorMessage: `The publishableKey passed to Clerk is invalid. You can get your Publishable key at https://dashboard.clerk.com/last-active?path=api-keys. (key={{key}})`, - MissingPublishableKeyErrorMessage: `Missing publishableKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.`, - MissingSecretKeyErrorMessage: `Missing secretKey. You can get your key at https://dashboard.clerk.com/last-active?path=api-keys.`, + InvalidPublishableKeyErrorMessage: `The publishableKey passed to Clerk is invalid (key={{key}}, expected format: pk_test_... or pk_live_...). To create a Clerk application with valid keys, in your terminal run: + + npx clerk@latest init + +It creates a Clerk application and writes keys to your .env file. Requires no Clerk account or login and the command is non-interactive. + +If you have a Clerk application, run \`npx clerk@latest env pull\` to write the keys (\`--instance prod\` for production keys). Or copy its Publishable key from https://dashboard.clerk.com/last-active?path=api-keys.`, + MissingPublishableKeyErrorMessage: `Missing publishableKey. To set up Clerk for this project, in your terminal run: + + npx clerk@latest init + +It creates a Clerk application and writes keys to your .env file. Requires no Clerk account or login and the command is non-interactive. + +If you have a Clerk application, run \`npx clerk@latest env pull\` to write the keys. Or copy them from https://dashboard.clerk.com/last-active?path=api-keys. Deploy a production instance by running \`npx clerk@latest deploy\`, or \`npx clerk@latest env pull --instance prod\` to use an existing one.`, + MissingSecretKeyErrorMessage: `Missing secretKey. To set up Clerk for this project, in your terminal run: + + npx clerk@latest init + +It creates a Clerk application and writes keys to your .env file. Requires no Clerk account or login and the command is non-interactive. + +If you have a Clerk application, run \`npx clerk@latest env pull\` to write the keys. Or copy them from https://dashboard.clerk.com/last-active?path=api-keys. Deploy a production instance by running \`npx clerk@latest deploy\`, or \`npx clerk@latest env pull --instance prod\` to use an existing one.`, MissingClerkProvider: `{{source}} can only be used within the component. Learn more: https://clerk.com/docs/components/clerk-provider`, }); diff --git a/packages/shared/src/keys.ts b/packages/shared/src/keys.ts index 389614332d9..74540337a39 100644 --- a/packages/shared/src/keys.ts +++ b/packages/shared/src/keys.ts @@ -98,6 +98,14 @@ function isValidDecodedPublishableKey(decoded: string): boolean { return withoutTrailing.includes('.'); } +const fatalKeyGuidance = `To create a Clerk application with valid keys, in your terminal run: + + npx clerk@latest init + +It creates a Clerk application and writes keys to your .env file. Requires no Clerk account or login and the command is non-interactive. + +If you have a Clerk application, run \`npx clerk@latest env pull\` to write the keys (\`--instance prod\` for production keys). Or copy them from https://dashboard.clerk.com/last-active?path=api-keys.`; + export function parsePublishableKey( key: string | undefined, options: ParsePublishableKeyOptions & { fatal: true }, @@ -127,12 +135,10 @@ export function parsePublishableKey( if (!key || !isPublishableKey(key)) { if (options.fatal && !key) { - throw new Error( - 'Publishable key is missing. Ensure that your publishable key is correctly configured. Double-check your environment configuration for your keys, or access them here: https://dashboard.clerk.com/last-active?path=api-keys', - ); + throw new Error(`Publishable key is missing. ${fatalKeyGuidance}`); } if (options.fatal && !isPublishableKey(key)) { - throw new Error('Publishable key not valid.'); + throw new Error(`Publishable key not valid (expected format: pk_test_... or pk_live_...). ${fatalKeyGuidance}`); } return null; } @@ -144,14 +150,14 @@ export function parsePublishableKey( decodedFrontendApi = isomorphicAtob(key.split('_')[2]); } catch { if (options.fatal) { - throw new Error('Publishable key not valid: Failed to decode key.'); + throw new Error(`Publishable key not valid: Failed to decode key. ${fatalKeyGuidance}`); } return null; } if (!isValidDecodedPublishableKey(decodedFrontendApi)) { if (options.fatal) { - throw new Error('Publishable key not valid: Decoded key has invalid format.'); + throw new Error(`Publishable key not valid: Decoded key has invalid format. ${fatalKeyGuidance}`); } return null; }