hydrogen icon indicating copy to clipboard operation
hydrogen copied to clipboard

removeChild called on a node that is not a child — missing null/ownership checks

Open jboutotte opened this issue 4 months ago • 0 comments

What is the location of your example repository?

I do not control the source — observed as an end-user

Which package or tool is having this issue?

Oxygen

What version of that package or tool are you using?

Shopify storefront hosted on Shopify Oxygen

What version of Remix are you using?

@remix-run/server-runtime v2.16.1 (inferred from compiled JS)

Steps to Reproduce

Environment Shopify storefront hosted on Shopify Oxygen Remix v2.16.1 (based on @remix-run/server-runtime) Asset: https://cdn.shopify.com/oxygen-v2/41356/35217/73898/2316767/assets/components-K_d28kzW.js Browser: Chrome Version 139.0.7258.157 I do not control the source — observed as an end-user

While using a Hydrogen storefront hosted on Shopify Oxygen, I observed the following runtime error: NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. at af (components-K_d28kzW.js:40:26247) at Qn (components-K_d28kzW.js:40:25934) at af (components-K_d28kzW.js:40:27020) at Qn (components-K_d28kzW.js:40:25934) at af (components-K_d28kzW.js:40:26695) at Qn (components-K_d28kzW.js:40:25934) at af (components-K_d28kzW.js:40:26695) at Jt (components-K_d28kzW.js:40:27549) at sf (components-K_d28kzW.js:40:27812) at Jt (components-K_d28kzW.js:40:27699)

The error occurs in the following (compiled) code within the asset: https://cdn.shopify.com/oxygen-v2/41356/35217/73898/2316767/assets/components-K_d28kzW.js

The error was found on this site: https://intl.drsquatch.com/de-eu/products/pine-tar-conditioner Although the same error occurred on most of the intl.drsquatch.com web pages. The error happened because the website is using another 3rd party component that manipulates the DOM structure for translations. I know this is a classic example of two 3rd party components conflicting with each other. The other 3rd party component has since adjusted their code so this error does not actually happen anymore. But it's clear that there is a flaw in the oxygen components-K_d28kzW.js code and this should be corrected to have defensive coding and can play well with other 3rd party components.

Relevant snippet from components-K_d28kzW.js (de-minified for clarity):

function af(e, t, n) {
    if (st && typeof st.onCommitFiberUnmount == "function")
        try {
            st.onCommitFiberUnmount($e, n)
        } catch {}
    switch (n.tag) {
    case 5:
        mt || Xr(n, t); 
    case 6:
        var l = ct
          , a = Yt; 
        ct = null,
        Qn(e, t, n), 
        ct = l,
        Yt = a,
        ct !== null && (Yt ? (e = ct, 
        n = n.stateNode,
        e.nodeType === 8 ? e.parentNode.removeChild(n) : e.removeChild(n)) : ct.removeChild(n.stateNode));  // <== ERROR OCCURS HERE
        break;
  1. The code does not check if the node is a child of the parent.
  2. The code does not check if any of the object references are null before using it.
  3. This leads to a runtime crash when the DOM structure doesn't match expectations.

Suggested Fix

function af(e, t, n) {
    if (st && typeof st.onCommitFiberUnmount == "function")
        try { 
            st.onCommitFiberUnmount($e, n)
        } catch {}
    switch (n.tag) {
    case 5:
        mt || Xr(n, t); 
    case 6:
        var l = ct,
            a = Yt;
        ct = null;
        Qn(e, t, n);
        ct = l;
        Yt = a;
        if (ct !== null) {
            if (Yt) {
                e = ct;
                n = n.stateNode;
                if (e.nodeType === 8) {
                    if (e.parentNode && n && e.parentNode.contains(n)) {
                        e.parentNode.removeChild(n);
                    }
                } else {
                    if (e && n && e.contains(n)) {
                        e.removeChild(n);
                    }
                }
            } else {
                if (ct && n && n.stateNode && ct.contains(n.stateNode))
                {
                    ct.removeChild(n.stateNode);
                }
            }    
        }
        break;

Expected Behavior

There are no javascript errors and the website menus appear and work properly.

Actual Behavior

The javascript throws the error NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. and the website menus do not appear.

jboutotte avatar Sep 12 '25 13:09 jboutotte