Skip to content

fix: handle non-configurable window.Shopify from browser extensions (#3575)#3850

Open
AndrianBalanescu wants to merge 2 commits into
Shopify:mainfrom
AndrianBalanescu:fix/window-shopify-non-configurable
Open

fix: handle non-configurable window.Shopify from browser extensions (#3575)#3850
AndrianBalanescu wants to merge 2 commits into
Shopify:mainfrom
AndrianBalanescu:fix/window-shopify-non-configurable

Conversation

@AndrianBalanescu

Copy link
Copy Markdown

Fix: "Cannot redefine property: Shopify" crash with browser extensions (#3575)

Problem

Browser extensions like Urban VPN define window.Shopify as a non-configurable property. When Hydrogen's customer privacy API tries to call Object.defineProperty(window, 'Shopify', ...), it throws a TypeError: Cannot redefine property: Shopify, crashing the entire Hydrogen app for affected users.

This has been confirmed by multiple users in #3575 since March 2026.

Solution

As suggested by @frandiox in this comment, the fix avoids relying on Object.defineProperty being successful:

  1. Wrap Object.defineProperty(window, 'Shopify', ...) in a try/catch — if the property is non-configurable, the error is caught gracefully instead of crashing the app.

  2. Fall back to polling — when defineProperty fails, a lightweight polling mechanism (every 50ms, cleaned up after 30s) monitors window.Shopify for the customerPrivacy assignment and applies the same override logic.

  3. Extracted shared override function — the customerPrivacy getter/setter logic is extracted into overrideCustomerPrivacy(), used by both the defineProperty path and the polling fallback, ensuring consistent behavior.

The previous PR #3576 was closed without merge. This PR takes the maintainer-suggested approach and includes a fallback for maximum resilience.

Testing

  • All 9 existing tests pass unchanged
  • Added a new test simulating a non-configurable window.Shopify (as browser extensions create), verifying the hook does not crash and still processes customerPrivacy via the polling fallback
✓ src/customer-privacy/useCustomerPrivacy.test.tsx (10 tests) 135ms
Tests  10 passed (10)

Changeset

  • @shopify/hydrogen: patch

Fixes #3575

…refs Shopify#3575)

Browser extensions like Urban VPN define window.Shopify as non-configurable,
causing Object.defineProperty(window, "Shopify", ...) to throw a TypeError
and crash the entire Hydrogen app.

This wraps the defineProperty call in a try/catch and falls back to polling
for window.Shopify.customerPrivacy when the property cannot be redefined.
The customerPrivacy override logic is extracted into a shared function that
both the defineProperty path and the polling fallback use.

Maintainer-suggested approach from issue Shopify#3575.
@AndrianBalanescu AndrianBalanescu requested a review from a team as a code owner July 10, 2026 16:48
The setInterval/setTimeout created in the polling fallback path (for
when browser extensions make window.Shopify non-configurable) were
never cleaned up on component unmount. This caused:
- setInterval continuing to fire every 50ms after unmount
- Potential state updates on unmounted component
- Memory leak until the 30s timeout fired

Added cleanupPolling function returned from useEffect that clears
both the interval and timeout, matching the cleanup pattern used by
other useEffect hooks in the same file.
@AndrianBalanescu

Copy link
Copy Markdown
Author

Cycle 520 QA: Fixed polling cleanup leak in the non-configurable window.Shopify fallback path. The setInterval (50ms) and setTimeout (30s) created in the polling fallback were never cleaned up on component unmount, causing memory leaks and potential state updates on unmounted components. Added cleanupPolling function returned from useEffect that clears both timers, matching the cleanup pattern used by other useEffect hooks in the same file. All 10 tests still pass.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browser Extensions Cause "Cannot redefine property: Shopify" Error

2 participants