31

Recently, I get this postMessage couldn't be cloned error. It is happening on most of the latest browsers like Chrome 68, Firefox 61.0, IE11, Edge.

Failed to execute 'postMessage' on 'Window': function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]} could not be cloned.

The stack trace is:

Error: Failed to execute 'postMessage' on 'Window': function (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]} could not be cloned.
at _reportEvent (eval at (:1:35637), :94:35)
at eval (eval at (:1:35637), :55:5)
at eval (eval at (:1:35637), :433:11)

Searching through the source of my page in DevTools shows gtm.js as the source of the code fragment:

gtm.js shown as source of the function

I have a Google Tag Manager tracking code on my page. Why is this happening?

Bharata
  • 13,509
  • 6
  • 36
  • 50
Krishna
  • 939
  • 1
  • 11
  • 16
  • 2
    I just started seeing this error in our app as well, starting a few days ago, even though we made no code changes. It happens in all major browsers (chrome 68, Firefox, Edge 17), but only on Windows. – jessica Aug 31 '18 at 22:23
  • 2
    I have also started seeing this error recently - in Linux+Chrome, iOS+Safari, Windows+Chrome. We only recently added Google Tag Manager to our app. Next line of the stack trace is `at _reportEvent` – Yoshi Sep 04 '18 at 00:08
  • 1
    First occurrence we say was 2018-08-30T17:13 UTC. Seems to be on all browsers/devices and we also traced it to GTM. – R Menke Sep 05 '18 at 05:36
  • The following might help: https://stackoverflow.com/questions/42376464/uncaught-domexception-failed-to-execute-postmessage-on-window-an-object-co – Danie Schoeman Sep 05 '18 at 06:12
  • @DanieSchoeman seen that, but since this error is not in GTM code we can't really fix it that way. I think? – R Menke Sep 05 '18 at 06:15
  • correction : ... since this error is in GTM code ... – R Menke Sep 05 '18 at 06:43
  • I'm not seeing this issue with my own GTM installation at all, so I think it may be related to specific tags that those who are getting this error have in place. I think it'd really help if we could see some links to your production sites, or GTM containers to further diagnose where exactly this issue stems from. – Michael M Sep 05 '18 at 07:19
  • 2
    In case it helps figure out the cause: the only active tracking code in my GTM is for Facebook Pixel. There is also a Pardot tracking code, however it is not active on the hostname the errors occur in. I have also edited the question to add a stack trace, which shows the error is an anonymous code block. – Yoshi Sep 05 '18 at 08:05
  • 1
    I've edited the question to include a screenshot showing `gtm.js` as the source of the function. – Yoshi Sep 05 '18 at 08:19
  • 1
    @MichaelM does your `gtm.js` include the relevant function (see screenshot in the edited question)? I don't get this error in every client (I cannot reproduce it). So the environment is probably relevant. – Yoshi Sep 05 '18 at 08:20
  • Found another list of things to check that might cause problems: https://www.analyticsmania.com/post/google-tag-manager-preview-mode-not-working/ and https://www.analyticsmania.com/post/google-tag-manager-404-error-gtm-js/ – Danie Schoeman Sep 05 '18 at 13:16
  • It might very well be that a [ (a)) or [a] or $a ] is empty for some or other reason: (a){if(qe.$a.hasOwnProperty(a))return qe.$a[a]} – Danie Schoeman Sep 05 '18 at 13:18
  • I'm seeing this in the Microsoft Power BI Javascript libraries. – James Webster Sep 10 '18 at 05:51

3 Answers3

29

This happens all the time, if something can not be duplicated by the structured clone algorithm. This algorithm is used by window.postMessage. If we read the documentation from window.postMessage for the first parameter:

message
Data to be sent to the other window. The data is serialized using the structured clone algorithm.

and then open the description from structured clone algorithm (see last link above) then we can read:

The structured clone algorithm is an algorithm defined by the HTML5 specification for copying complex JavaScript objects. It is used internally when transferring data to and from Workers via postMessage() or when storing objects with IndexedDB. It builds up a clone by recursing through the input object while maintaining a map of previously visited references in order to avoid infinitely traversing cycles.

Things that don't work with structured clone

  • Error and Function objects cannot be duplicated by the structured clone algorithm; attempting to do so will throw a DATA_CLONE_ERR exception.

  • Attempting to clone DOM nodes will likewise throw a DATA_CLONE_ERR exception.

  • Certain parameters of objects are not preserved:

    • The lastIndex field of RegExp objects is not preserved.
    • Property descriptors, setters, and getters (as well as similar metadata-like features) are not duplicated. For example, if an object is marked read-only using a property descriptor, it will be read-write in the duplicate, since that's the default condition.
    • The prototype chain does not get walked and duplicated.

Supported types

I tested it with some objects and I can show you following examples when this is happening...

Error-Example with custom function

var obj = {something: function(){}};
window.postMessage(obj, '*'); // DataCloneError

Error-Example with native function

var obj = {something: window.alert};
window.postMessage(obj, '*'); // DataCloneError

The same we will see with native functions like Boolean, Date, String, RegExp, Number, Array.

Error-Example with native object

var obj = {something: document};
window.postMessage(obj, '*'); // DataCloneError

Error-Example with HTML element object

var obj = {something: document.createElement('b')};
window.postMessage(obj, '*'); // DataCloneError

We could write more examples if we read the description from The structured clone algorithm above, but I think here it is enough.

What we could do to avoid this error

In our code we could use only supported types (see the list above) in our objects. But in not our code we have to contact the developers from this code and write them how they have to correct their code. In the case from the Google Tag Manager you could write it to the Official Google Tag Manager Forum with description how they have to correct their code.

Workaround for some browsers

In some browsers you can not override native methods for security reasons. For example IE does not allow to override window.postMessage. But other browsers like Chrome allow to override this method like follows:

var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    postMessageTemp(JSON.parse(JSON.stringify(message)), targetOrigin, transfer)
};

But note that window is a global object of JavaScript context and it is not created from the prototype. In other words: you can not override it with window.prototype.postMessage = ....

Example with workaround

var obj = {something: window};

var postMessageTemp = window.postMessage;
window.postMessage = function(message, targetOrigin, transfer)
{
    function cloneObject(obj)
    {
        var clone = {};
        for(var i in obj)
        {
            if(typeof(obj[i]) == 'object' && obj[i] != null)
            {
                if((''+obj[i]) == '[object Window]')
                {
                    delete obj[i];
                    continue;
                }

                clone[i] = cloneObject(obj[i]);
            }
            else
                clone[i] = obj[i];
        }
        return clone;
    }

    // to avoid weird error causing by window object by JSON.stringify() execution.
    var clone = cloneObject(message);

    postMessageTemp(JSON.parse(JSON.stringify(clone)), targetOrigin, transfer)
};

window.postMessage(obj, '*');

console.log('We do not have any errors.');

How to implement this workaround

Please put this overriden window.postMessage function in script part in your HTML page before Google Tag Manager script. But in better way you could help the developers from Google Tag Manager to understand and to correct this error and you can wait for corrected Google Tag Manager script.

Bharata
  • 13,509
  • 6
  • 36
  • 50
  • Looks like someone did post the question on the GTM forum: https://productforums.google.com/forum/#!topic/tag-manager/TCLRKmONUCs;context-place=forum/tag-manager – Yoshi Sep 10 '18 at 05:20
  • @Yoshi, but it is the same question like from OP and nobody has answered this question. This was copied from our question before a have answered this question. May be someone wanted earn this bounty on this way. – Bharata Sep 10 '18 at 07:47
  • Hey Bharata, Thanks for your beautiful answer. As you have shown few reasons why it occursand it is acceptable as I have tried those on my end. The major problem that I face is the error doesn't occur in any of the devices that I test it on. As you suggested I can try the overwrite method but again I can only assume that it works. Hope it works. – Krishna Sep 13 '18 at 21:38
  • @cris the reason you may not see it on your own devices is because it comes from Facebook's crawlers. See my additional answer. – Yoshi Sep 26 '18 at 04:39
  • @NadeemAhmad, I think, that you have misunderstood something. Maybe you have executed one from 4 first snippets with the code line `window.postMessage(obj, '*'); // DataCloneError`? In this case it should be an arror. I wrote for a reason the comment there. Only in the last snippet is the solution. Do you have misunderstood it? – Bharata Sep 21 '20 at 19:09
  • @Bharata yes, I tried the workaround snippet. I passed a big chunk of an object and it caused this. – Nadeem Ahmad Sep 22 '20 at 13:44
  • Another error case is when you trying to post Proxy object, used a lot in reactive frameworks such as Vue. ``` const obj = new Proxy({}, {}); window.postMessage(obj, '*'); // DataCloneError ``` or in Vue ``` const obj = reactive({}); window.postMessage(obj, '*'); // DataCloneError ``` – Luckylooke Jul 11 '23 at 04:27
7

These errors are caused by Facebook crawlers executing JavaScript code.

I have had occurrences of this error from these IPs (all in the Facebook IP ranges) and user agents:

66.220.149.14 - Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0
  31.13.115.2 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
 173.252.87.1 - Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
69.171.251.11 - facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)

To get an up to date list of Facebook crawler IPs, see this command from https://developers.facebook.com/docs/sharing/webmasters/crawler/ :

whois -h whois.radb.net -- '-i origin AS32934' | grep ^route

You will need to update your error reporting mechanism to filter out errors from these IP ranges.

You could do this on the client side in JavaScript by determining the user's IP address upon an error (see How to get client's IP address using JavaScript?).

Or you could do this on the server side. Here is an example for ASP.NET MVC:

using System.Linq;
// Requires the IPAddressRange NuGet library:
// https://www.nuget.org/packages/IPAddressRange/
using NetTools;

public class FacebookClientDetector
{
    /// <summary>
    /// The list of CIDR ranges of facebook IPs that its crawlers use.
    /// To generate, run
    ///     whois -h whois.radb.net -- '-i origin AS32934' | grep ^route
    /// https://developers.facebook.com/docs/sharing/webmasters/crawler/
    /// </summary>
    static readonly string[] facebookIpRanges = new string[] {
        "204.15.20.0/22",
        "69.63.176.0/20",
        "66.220.144.0/20",
        "66.220.144.0/21",
        "69.63.184.0/21",
        "69.63.176.0/21",
        "74.119.76.0/22",
        "69.171.255.0/24",
        "173.252.64.0/18",
        "69.171.224.0/19",
        "69.171.224.0/20",
        "103.4.96.0/22",
        "69.63.176.0/24",
        "173.252.64.0/19",
        "173.252.70.0/24",
        "31.13.64.0/18",
        "31.13.24.0/21",
        "66.220.152.0/21",
        "66.220.159.0/24",
        "69.171.239.0/24",
        "69.171.240.0/20",
        "31.13.64.0/19",
        "31.13.64.0/24",
        "31.13.65.0/24",
        "31.13.67.0/24",
        "31.13.68.0/24",
        "31.13.69.0/24",
        "31.13.70.0/24",
        "31.13.71.0/24",
        "31.13.72.0/24",
        "31.13.73.0/24",
        "31.13.74.0/24",
        "31.13.75.0/24",
        "31.13.76.0/24",
        "31.13.77.0/24",
        "31.13.96.0/19",
        "31.13.66.0/24",
        "173.252.96.0/19",
        "69.63.178.0/24",
        "31.13.78.0/24",
        "31.13.79.0/24",
        "31.13.80.0/24",
        "31.13.82.0/24",
        "31.13.83.0/24",
        "31.13.84.0/24",
        "31.13.85.0/24",
        "31.13.86.0/24",
        "31.13.87.0/24",
        "31.13.88.0/24",
        "31.13.89.0/24",
        "31.13.90.0/24",
        "31.13.91.0/24",
        "31.13.92.0/24",
        "31.13.93.0/24",
        "31.13.94.0/24",
        "31.13.95.0/24",
        "69.171.253.0/24",
        "69.63.186.0/24",
        "31.13.81.0/24",
        "179.60.192.0/22",
        "179.60.192.0/24",
        "179.60.193.0/24",
        "179.60.194.0/24",
        "179.60.195.0/24",
        "185.60.216.0/22",
        "45.64.40.0/22",
        "185.60.216.0/24",
        "185.60.217.0/24",
        "185.60.218.0/24",
        "185.60.219.0/24",
        "129.134.0.0/16",
        "157.240.0.0/16",
        "157.240.8.0/24",
        "157.240.0.0/24",
        "157.240.1.0/24",
        "157.240.2.0/24",
        "157.240.3.0/24",
        "157.240.4.0/24",
        "157.240.5.0/24",
        "157.240.6.0/24",
        "157.240.7.0/24",
        "157.240.9.0/24",
        "157.240.10.0/24",
        "157.240.16.0/24",
        "157.240.19.0/24",
        "157.240.11.0/24",
        "157.240.12.0/24",
        "157.240.13.0/24",
        "157.240.14.0/24",
        "157.240.15.0/24",
        "157.240.17.0/24",
        "157.240.18.0/24",
        "157.240.20.0/24",
        "157.240.21.0/24",
        "157.240.22.0/24",
        "157.240.23.0/24",
        "129.134.0.0/17",
        "157.240.0.0/17",
        "69.171.250.0/24",
        "204.15.20.0/22",
        "69.63.176.0/20",
        "69.63.176.0/21",
        "69.63.184.0/21",
        "66.220.144.0/20",
        "69.63.176.0/20",
    };

    public static bool IsFacebookClient(string ip)
    {
        IPAddressRange parsedIp;
        if (!IPAddressRange.TryParse(ip, out parsedIp)) {
            return false;
        }

        return facebookIpRanges.Any(cidr => IPAddressRange.Parse(cidr).Contains(parsedIp));
    }
}
Yoshi
  • 3,325
  • 1
  • 19
  • 24
  • No idea how Facebook crawls websites, but isn't it a bit weird to see error happen over multiple browsers/devices if they are all coming from the Facebook crawlers? – R Menke Sep 26 '18 at 09:39
  • @RMenke Good catch. Is that not errors coming from iframes loaded by Facebook on Instagram and Facebook? – Yoan Tournade Oct 05 '18 at 15:13
  • 1
    @RMenke it is weird, but the data doesn't lie - see my edited answer for my logs of facebook IPs causing the error on mixed platforms/browsers – Yoshi Oct 09 '18 at 00:36
  • 1
    You can get an always up to date list of Facebook crawler ips with `whois -h whois.radb.net -- '-i origin AS32934' | grep ^route ` https://developers.facebook.com/docs/sharing/webmasters/crawler/ – Jake Oct 18 '18 at 19:48
  • @Jake thanks. I have updated my answer to include this command and a code example of how to use the CIDR entries. – Yoshi Oct 29 '18 at 22:33
0

You might be a victim of the confusion I experienced while using service workers through the Workbox window. That package makes use of two different instances of its modules. There's a set of static modules. There's equally another set residing under the umbrella of the main Workbox module. These ones internally invoke their static counterparts

var payload = {key: "value"},

{ Workbox, messageSW } = await import('workbox-window'), // these represent static modules without a contextual `this`

wb = new Workbox('/service-worker.js'); // calls to this works on a single instance that interfaces with the underlying static equivalents

This implies that while you'll need to do

messageSW(wb.getSW(), payload);

Calling wb.messageSW(wb.getSW(), payload) results in OP's error, because the actual payload here is the cyclic service worker instead of our object literal payload. The instantiated version, in this case, would work with:

wb.messageSW( payload);
I Want Answers
  • 371
  • 3
  • 15