Skip to content

Fix event subscription handle validation in stable/4.9 - #8849

Merged
isaacroldan merged 1 commit into
stable/4.9from
codex/fix-event-subscription-handles-stable-4-9
Oct 9, 2026
Merged

isaacroldan merged 1 commit into
stable/4.9from
codex/fix-event-subscription-handles-stable-4-9

Conversation

@isaacroldan

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Backport of #8848 to stable/4.9.

Event subscription handles use UI extension handle rules, which reject values such as orders_create, -orders, and orders-.

WHAT is this pull request doing?

Use a separate event subscription handle schema that accepts alphanumeric characters, underscores, and hyphens. Keep whitespace trimming, the 50-character limit, and rejection of empty handles and the reserved events handle. Update the tests and add a patch changeset.

Validation: 63 focused tests passed, including the event loader tests. TypeScript, changed-file ESLint, and git diff --check passed. Four new acceptance cases fail when the fix is removed.

How to manually test your changes?

  1. In an existing app, set a [[events.subscription]] handle in shopify.app.toml to orders_create.
  2. From this checkout, run pnpm shopify app build --path /path/to/app and confirm there is no handle validation error.
  3. Repeat with -orders and orders-.
  4. Set the handle to orders/create or events and confirm configuration validation fails.

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 — added a patch changeset for this bug fix

@isaacroldan
isaacroldan requested a review from a team as a code owner October 9, 2026 09:00
@github-actions github-actions Bot added the Area: @shopify/cli @shopify/cli package issues label Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/constants.d.ts
@@ -8,7 +8,6 @@ export declare const environmentVariables: {
     env: string;
     noAnalytics: string;
     optOutInstrumentation: string;
-    organizationAutomationToken: string;
     appAutomationToken: string;
     partnersToken: string;
     runAsUser: string;
packages/cli-kit/dist/public/node/environment.d.ts
@@ -10,13 +10,10 @@
  */
 export declare function getEnvironmentVariables(): NodeJS.ProcessEnv;
 /**
- * Returns the automation token the CLI authenticates with, from the first of these variables that is set:
- * SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN, SHOPIFY_APP_AUTOMATION_TOKEN, or the deprecated SHOPIFY_CLI_PARTNERS_TOKEN.
+ * Returns the value of the SHOPIFY_APP_AUTOMATION_TOKEN environment variable,
+ * falling back to the deprecated SHOPIFY_CLI_PARTNERS_TOKEN.
  *
- * Returns undefined when the variables can't be used (an empty value, or the organization variable set alongside
- * another one). Callers then fall back to the login flow, which reports the problem instead of logging in.
- *
- * @returns The automation token, or undefined if there is no usable one.
+ * @returns The app automation token value, or undefined if neither env var is set.
  */
 export declare function getAppAutomationToken(): string | undefined;
 /**
packages/cli-kit/dist/public/node/session.d.ts
@@ -128,35 +128,14 @@ export declare function ensureAuthenticatedAdmin(store: string, scopes?: AdminAP
  * @returns The access token and store.
  */
 export declare function ensureAuthenticatedThemes(store: string, password: string | undefined, scopes?: AdminAPIScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<AdminSession>;
-/**
- * Options for `ensureAuthenticatedBusinessPlatform`.
- */
-export interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthenticatedAdditionalOptions {
-    /**
-     * Authenticate with the automation token set in the environment, when there is one, instead of the
-     * logged-in user. Only commands that support automation tokens opt in; other callers, such as
-     * Hydrogen's login, keep using the user's session.
-     */
-    allowAutomationToken?: boolean;
-}
 /**
  * Ensure that we have a valid session to access the Business Platform API.
  *
- * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.
+ * @param scopes - Optional array of extra scopes to authenticate with.
  * @param options - Optional extra options to use.
  * @returns The access token for the Business Platform API.
  */
-export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedBusinessPlatformOptions): Promise<string>;
-/**
- * Fails when SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN is set, for commands that can't run with that token.
- *
- * Organization automation tokens can't log in as a user or call a store's Admin API, so commands that need either
- * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any
- * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.
- *
- * @throws AbortError when SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN is set.
- */
-export declare function ensureNoOrganizationAutomationToken(): void;
+export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<string>;
 /**
  * Logout from Shopify.
  *
packages/cli-kit/dist/private/node/session/exchange.d.ts
@@ -45,10 +45,9 @@ export declare function exchangeAppAutomationTokenForAppManagementAccessToken(to
 /**
  * Given a custom app automation token passed as ENV variable, request a valid Business Platform API token.
  * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`
- * @param scopes - The scopes to request. An empty list makes Identity issue every Business Platform scope the token holds.
  * @returns An instance with the application access tokens.
  */
-export declare function exchangeAppAutomationTokenForBusinessPlatformAccessToken(token: string, scopes?: string[]): Promise<{
+export declare function exchangeAppAutomationTokenForBusinessPlatformAccessToken(token: string): Promise<{
     accessToken: string;
     userId: string;
 }>;
packages/cli-kit/dist/public/node/error/schema.d.ts
@@ -30,24 +30,24 @@ export declare const JsonAbortErrorSchema: zod.ZodObject<{
     type: "abort";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "abort";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>;
 export declare const JsonBugErrorSchema: zod.ZodObject<{
     stack: zod.ZodOptional<zod.ZodString>;
@@ -71,7 +71,6 @@ export declare const JsonBugErrorSchema: zod.ZodObject<{
     type: "bug";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     stack?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
@@ -79,11 +78,11 @@ export declare const JsonBugErrorSchema: zod.ZodObject<{
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "bug";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     stack?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
@@ -91,6 +90,7 @@ export declare const JsonBugErrorSchema: zod.ZodObject<{
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>;
 export declare const JsonExternalErrorSchema: zod.ZodObject<{
     command: zod.ZodString;
@@ -117,26 +117,26 @@ export declare const JsonExternalErrorSchema: zod.ZodObject<{
     command: string;
     args: string[];
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "external";
     message: string;
     command: string;
     args: string[];
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>;
 export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
     message: zod.ZodString;
@@ -159,24 +159,24 @@ export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
     type: "abort";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "abort";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>, zod.ZodObject<{
     stack: zod.ZodOptional<zod.ZodString>;
     message: zod.ZodString;
@@ -199,7 +199,6 @@ export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
     type: "bug";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     stack?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
@@ -207,11 +206,11 @@ export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "bug";
     message: string;
     code?: string | undefined;
-    tryMessage?: string | undefined;
     stack?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
@@ -219,6 +218,7 @@ export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>, zod.ZodObject<{
     command: zod.ZodString;
     args: zod.ZodArray<zod.ZodString, "many">;
@@ -244,26 +244,26 @@ export declare const JsonErrorSchema: zod.ZodUnion<[zod.ZodObject<{
     command: string;
     args: string[];
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }, {
     type: "external";
     message: string;
     command: string;
     args: string[];
     code?: string | undefined;
-    tryMessage?: string | undefined;
     nextSteps?: string[] | undefined;
     customSections?: {
         body: string | string[][];
         title?: string | undefined;
     }[] | undefined;
     details?: unknown;
+    tryMessage?: string | undefined;
 }>]>;
 export declare const jsonErrorOutputSchema: import("../json-output-schema.js").JsonOutputSchema<zod.ZodObject<{
     error: zod.ZodUnion<[zod.ZodObject<{
@@ -287,24 +287,24 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
         type: "abort";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }, {
         type: "abort";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }>, zod.ZodObject<{
         stack: zod.ZodOptional<zod.ZodString>;
         message: zod.ZodString;
@@ -327,7 +327,6 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
         type: "bug";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         stack?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
@@ -335,11 +334,11 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }, {
         type: "bug";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         stack?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
@@ -347,6 +346,7 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }>, zod.ZodObject<{
         command: zod.ZodString;
         args: zod.ZodArray<zod.ZodString, "many">;
@@ -372,44 +372,43 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
         command: string;
         args: string[];
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }, {
         type: "external";
         message: string;
         command: string;
         args: string[];
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     }>]>;
 }, "strict", zod.ZodTypeAny, {
     error: {
         type: "abort";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     } | {
         type: "bug";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         stack?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
@@ -417,37 +416,37 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     } | {
         type: "external";
         message: string;
         command: string;
         args: string[];
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     };
 }, {
     error: {
         type: "abort";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     } | {
         type: "bug";
         message: string;
         code?: string | undefined;
-        tryMessage?: string | undefined;
         stack?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
@@ -455,18 +454,19 @@ export declare const jsonErrorOutputSchema: import("../json-output-schema.js").J
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     } | {
         type: "external";
         message: string;
         command: string;
         args: string[];
         code?: string | undefined;
-        tryMessage?: string | undefined;
         nextSteps?: string[] | undefined;
         customSections?: {
             body: string | string[][];
             title?: string | undefined;
         }[] | undefined;
         details?: unknown;
+        tryMessage?: string | undefined;
     };
 }>>;
\ No newline at end of file

@isaacroldan

Copy link
Copy Markdown
Contributor Author

/snapit

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @isaacroldan! Your snapshot has been published to npm.

Built from a289e1cfc6da3f2bf1b6213c7ffa0aa66f47097d. Workflow run.

Test the snapshot by installing your package globally:

pnpm i -g --@shopify:registry=https://registry.npmjs.org @shopify/[email protected]

Caution

After installing, validate the version by running shopify version in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

@isaacroldan
isaacroldan merged commit b2bf4db into stable/4.9 Oct 9, 2026
27 of 29 checks passed
@isaacroldan
isaacroldan deleted the codex/fix-event-subscription-handles-stable-4-9 branch October 9, 2026 09:38
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.

2 participants