sessionToken.get() crashing unconditionally

shopify.sessionToken.get() crashes with “Cannot add property __, object is not extensible” in checkout UI extension (api_version 2026-07)

Summary

Calling await shopify.sessionToken.get() anywhere inside a checkout UI extension (native Preact / Polaris web-components target, purchase.thank-you.block.render) crashes the extension with:

Uncaught (in promise) ExtensionUsageError: TypeError: Cannot add property __, object is not extensible

The crash is not catchable from extension code — wrapping the call in try/catch does not catch it, so there is no application-level workaround. Confirmed on both a shopify app dev preview session and a genuinely deployed/released extension version (shopify app deploy) — identical error, identical compiled bundle hash — so this is not a dev-preview-only artifact.

Environment

  • Shopify CLI: 4.8.0
  • Extension api_version: 2026-07
  • Extension type: ui_extension, target purchase.thank-you.block.render
  • Node: v26.7.0
  • @shopify/ui-extensions: 2026.7.0
  • preact: 10.28.0 (also reproduced on ^10.10.x before pinning)
  • @preact/signals: not installed in the final repro (removed — see “Ruled out” below); also reproduced with 2.5.1 installed
  • Capability: network_access = true (approved via Partner Dashboard → API access → “Allow network access in checkout UI extensions”)

Minimal repro

extensions/thank-you-location/shopify.extension.toml:

api_version = "2026-07"

[[extensions]]

name = "location request"

handle = "thank-you-location"

type = "ui_extension"

[extensions.capabilities]

network_access = true

[[extensions.targeting]]

module = "./src/ThankYou.jsx"

target = "purchase.thank-you.block.render"

extensions/thank-you-location/src/ThankYou.jsx:

import { render } from 'preact';

export default async () => {

let debugMessage = "sessionToken.get() succeeded";

try {

await shopify.sessionToken.get();

} catch (err) {

debugMessage = "sessionToken.get() threw: " + (err?.message || String(err));

}

render(<DebugBanner message={debugMessage} />, document.body);

};

function DebugBanner({ message }) {

return (

<s-banner heading="debug">

<s-text>{message}</s-text>

</s-banner>

);

}

extensions/thank-you-location/package.json:

{

"name": "thank-you-location",

"version": "0.1.0",

"private": true,

"scripts": { "build": "shopify app build" },

"dependencies": {

"preact": "10.28.0",

"@shopify/ui-extensions": "2026.7.x"

}

}

Steps to reproduce

  1. Scaffold the extension above in a Shopify app (React Router app template), run npm run dev (shopify app dev).
  2. Add the block to the checkout’s Thank You page in the checkout editor and save.
  3. Place a test order on the dev store.
  4. Open the Thank You page’s browser console.

Observed: the crash below, before the debug banner ever renders. Expected: the debug banner renders with “sessionToken.get() succeeded” (or a caught rejection message, if the token legitimately fails for some environment reason).

Also reproduced by running shopify app deploy, releasing the new version (after granting network_access capability approval in the Partner Dashboard), stopping shopify app dev entirely, and placing a fresh order against the live, non-preview checkout — identical crash.

Full error

Uncaught (in promise) ExtensionUsageError: TypeError: Cannot add property __, object is not extensible

at eval (eval at _evalExtensionSource (data:text/javascript,...), <anonymous>:8:149912)

at Generator.throw (<anonymous>)

at rejected (eval at _evalExtensionSource (data:text/javascript,...), <anonymous>:8:2062)

Caused by: TypeError: Cannot add property __, object is not extensible

at In (8b5fe6a4846d3b8121cbb2fa09614bca.js:38:2831)

at Yt (8b5fe6a4846d3b8121cbb2fa09614bca.js:38:2052)

at at (8b5fe6a4846d3b8121cbb2fa09614bca.js:38:6755)

at Gt (8b5fe6a4846d3b8121cbb2fa09614bca.js:38:9735)

at eval (8b5fe6a4846d3b8121cbb2fa09614bca.js:38:11136)

at Generator.next (<anonymous>)

at l (8b5fe6a4846d3b8121cbb2fa09614bca.js:1:970)

(Function names are minified and change per build; the shape — four nested calls into the compiled extension bundle, then Generator.next/l — has been consistent across every build tested.)

What’s been ruled out

Starting from a more complete version of this extension (which also called fetch() after the token, and originally used useState/useEffect to show a loading spinner before the result), the following were each tried and each produced the identical crash signature, which is what narrowed this down to sessionToken.get() itself:

  1. Added @preact/signals as an explicit dependency (it’s part of Shopify’s own migration-guide “required trio” — preact, @preact/signals, @shopify/ui-extensions — but wasn’t in our original package.json).
  2. Flattened three structurally-different <s-banner> subtrees (loading/error/ready, swapped via useState) into one stable tree with only conditional children.
  3. Pinned preact@10.28.0 + @preact/signals@2.5.1 — versions confirmed by a Shopify developer community thread to fix a related useState reactivity bug (different symptom: stale state, not a crash). No effect here.
  4. Eliminated all conditional mounting via <s-box display=.../> toggles instead of {cond && <X/>}, so no <s-*> element ever mounts/unmounts after the first paint.
  5. Eliminated useState/useEffect entirely — did the async work before the one and only render() call, so Preact never performs a second render/diff pass at all. Still crashed, and the stack showed the crash now happens before render() is ever called.
  6. Removed import '@shopify/ui-extensions/preact' (whose only effect, per its source, is the module-load side effect shopify.setSignals(Signal), wiring Shopify’s reactive host data into @preact/signals) — component used no signals-based reactivity at that point, so this was untested-but-plausible dead code. No effect on the crash.
  7. Stripped the extension to nothing but shopify.sessionToken.get() in a try/catch (the repro above). Still crashes, uncatchably.
  8. Ruled out dev-preview as a factor by deploying and releasing a real, published version and testing against a live, non-preview checkout. Identical crash, identical compiled bundle hash as the dev-preview test.

Impact

sessionToken.get() is documented as the canonical way to authenticate a checkout-extension → app-backend request (see Session Token API). With it uncatchably crashing the extension, there is no way for a checkout UI extension on this API version to make an authenticated fetch() call to its own backend — a core, common use case.

Hey @Mohamed_ElEzaby, if possible, could you rebuild and deploy the same minimal extension using Node 22 LTS, then test whether the same call fails with api_version = "2026-04"? This will help us rule out Node 26 as a build variable and determine whether the behaviour is specific to 2026-07.

Thank you @KyleG-Shopify , results: (1) Node: the same extension built under Node 22.23.3 and 26.7.0 gives a byte-identical bundle (same SHA-256), so Node isn’t a variable. (2) Under shopify app dev preview, both api_version 2026-07 and 2026-04 throw ExtensionUsageError: TypeError: Cannot add property __, object is not extensible. (3) Correction to my original report: it isn’t sessionToken.get(). Logging shows the throw happens in the first Preact render() call, before sessionToken.get() is reached. I also previously said released builds crash too; that was wrong because the dev preview was still active. A properly released build ran without the error and sessionToken.get() resolved. (4) Open: on the released build no error is thrown but the rendered banner isn’t visible.

Summary

A minimal checkout UI extension (purchase.thank-you.block.render, Preact + native Polaris web components) throws

Uncaught (in promise) ExtensionUsageError: TypeError: Cannot add property __, object is not extensible

when served by the shopify app dev preview. Console logging added to the extension shows the throw happens in the first render(...) call, before shopify.sessionToken.get() is reached. The same source, released with shopify app deploy, ran without any ExtensionUsageError, and sessionToken.get() resolved normally.

Open issue on the released build: no error, but the rendered banner was not visible on the Thank You page (see “Not established”).

Environment

  • Shopify CLI 4.8.4 (project-local), Node 22.23.3 for all builds in the matrix
  • @shopify/ui-extensions 2026.7.0 (2026.4.4 for the 2026-04 run)
  • preact 10.28.0 for run A/B/C; 10.29.8 + @preact/signals 2.11.3 for D/E
  • Extension-only app (no web app, no scopes), network_access = true
  • Dev store with checkout extensibility, target purchase.thank-you.block.render

Node is not a factor

The extension bundle built under Node 22.23.3 and under Node 26.7.0 is byte-identical:

35e87fe4ef5fe1bda9568a5f1cc871b0e1697c29f1140fbaf8b78c604d47c2b4  thank-you-location.js  (Node 22.23.3)

35e87fe4ef5fe1bda9568a5f1cc871b0e1697c29f1140fbaf8b78c604d47c2b4 thank-you-location.js (Node 26.7.0)

Test matrix (same extension logic throughout)

Source (instrumented):

import { render } from 'preact';

export default async () => {

console.log("PINDROP_REPRO: extension started");

render(<DebugBanner message="1/3 extension started, calling sessionToken.get()..." />, document.body);

let message = "3/3 sessionToken.get() succeeded";

try {

console.log("PINDROP_REPRO: calling sessionToken.get()");

await shopify.sessionToken.get();

console.log("PINDROP_REPRO: sessionToken.get() resolved");

} catch (err) {

console.log("PINDROP_REPRO: sessionToken.get() threw", err?.message || String(err));

message = "3/3 sessionToken.get() threw: " + (err?.message || String(err));

}

render(<DebugBanner message={message} />, document.body);

console.log("PINDROP_REPRO: second render done");

};

function DebugBanner({ message }) {

return <s-banner heading="debug"><s-text>{message}</s-text></s-banner>;

}

Run Served via api_version Observed
A Released version pindrop-repro-2 (bare sessionToken.get() version) 2026-07 Bundle loaded from extensions.shopifycdn.com/.../version/pindrop-repro-2/``.... No ExtensionUsageError. Banner not visible.
B Released version pindrop-repro-3 (instrumented) 2026-07 Console: extension started, calling sessionToken.get(), sessionToken.get() resolved, second render done. No ExtensionUsageError. Banner not visible.
C shopify app dev preview, real order 2026-07 Console: only extension started, then ExtensionUsageError ... Cannot add property __. Never reaches calling sessionToken.get().
D shopify app dev preview, checkout editor, plus import '@shopify/ui-extensions/preact' and @preact/signals (official template shape) 2026-07 Same as C: only extension started, then the error.
E shopify app dev preview, checkout editor 2026-04 (@shopify/ui-extensions 2026.4.4) ExtensionUsageError ... Cannot add property __ observed; CLI log confirms the 2026-04 config was pushed to the dev preview. (PINDROP_REPRO lines were not captured for this run.)

Served-bundle URL patterns, for telling the two apart:

  • Dev preview: cdn.shopify.com/partners-extensions-scripts-bucket/ui_extension/handle/thank-you-location/version/dev-<uuid>/...
  • Released: extensions.shopifycdn.com/partners-extensions-scripts-bucket/ui_extension/handle/thank-you-location/version/pindrop-repro-<n>/...

Stack (run C, minified; frames inside the extension bundle)

Uncaught (in promise) ExtensionUsageError: TypeError: Cannot add property __, object is not extensible

at eval (eval at _evalExtensionSource (data:text/javascript,...), <anonymous>:4:155277)

at Generator.throw (<anonymous>)

at rejected (eval at _evalExtensionSource ..., <anonymous>:4:2201)

Caused by: TypeError: Cannot add property __, object is not extensible

at Fn (4a8dd70....js:38:2831)

at Bt (4a8dd70....js:38:2052)

at at (4a8dd70....js:38:6755)

at it (4a8dd70....js:38:9735)

at eval (4a8dd70....js:38:10903)

at Generator.next (<anonymous>)

A sourcemapped trace from an earlier run points into Preact’s own sources (children.js, index.js, render.js), consistent with a Preact internal property (__) being written to an object the preview sandbox has made non-extensible.

Not established

  • Why the banner is not visible on the released build even though the extension ran twice through render() without errors.
  • Whether the CLI’s unmodified checkout_ui template also throws under the dev preview (it was copied into the app but never placed in a checkout during these tests).
  • Whether the preview crash depends on the block being on the Thank You page versus a checkout-page block.

How to reproduce

  1. shopify app init --template none, add one ui_extension with the source above, target purchase.thank-you.block.render, api_version = "2026-07".
  2. npm install (Node 22), shopify app dev --store <dev store>.
  3. Add the block to the Thank You page in the checkout editor, open DevTools Console, refresh the editor preview (or place an order and stay on the Thank You page). Filter Console by PINDROP_REPRO and ExtensionUsage.
  4. For the released comparison: stop dev, run shopify app dev clean, run shopify app deploy, place a fresh order, and filter the same way. Note that shopify app dev leaves the preview active on the store after you stop it until shopify app dev clean is run.

Methodology notes (things that misled earlier runs)

  • The Network panel only records requests made after DevTools is opened. The extension script is fetched once, as the Thank You page loads, so DevTools must be open before payment (with Preserve log). The Console does keep earlier messages.
  • Reloading a Thank You page redirects to the customer-account order page, where this block does not run.

Thanks @Mohamed_ElEzaby, that test matrix settles most of it.

Testing further, I rebuilt your minimal extension on a fully independent setup (separate app, store, CLI, and Node versions) and the preview did not crash. It’s looking like the trigger is something in your app or store setup.

Can you run a fully fresh dev session: stop shopify app dev, remove the block, restart, re-add it, and place a fresh order. If it still crashes, a second dev store would rule the store itself out.

On the released build, place a fresh order with DevTools open before payment (Preserve log on), confirm the block is still placed in the checkout editor, and try a minimal single-s-text render if it is still invisible.

Thank you so much @KyleG-Shopify for testing this independently. You were right that it was our setup. The crash came from the extension missing a tsconfig.json with "jsxImportSource": "preact". Without it, the bundler used React’s JSX runtime, which in the dev preview freezes elements (Object.freeze). Preact then threw Cannot add property __, object is not extensible when it tried to write to them. The released build used React’s production runtime, which doesn’t freeze, but Preact couldn’t read those elements, so nothing rendered. That’s why it ran with no error and showed no banner. After adding the template’s tsconfig.json, the preview renders correctly. Sorry for the noise, and thanks for the pointers.