Chrome sendrequest error: TypeError: Converting circular structure to JSON
Asked Answered
F

18

616

I've got the following...

chrome.extension.sendRequest({
  req: "getDocument",
  docu: pagedoc,
  name: 'name'
}, function(response){
  var efjs = response.reply;
});

which calls the following..

case "getBrowserForDocumentAttribute":
  alert("ZOMG HERE");
  sendResponse({
    reply: getBrowserForDocumentAttribute(request.docu,request.name)
  });
  break;

However, my code never reaches "ZOMG HERE" but rather throws the following error while running chrome.extension.sendRequest

 Uncaught TypeError: Converting circular structure to JSON
 chromeHidden.JSON.stringify
 chrome.Port.postMessage
 chrome.initExtension.chrome.extension.sendRequest
 suggestQuery

Does anyone have any idea what is causing this?

Franglais answered 27/1, 2011 at 12:7 Comment(4)
You are trying to send an object that has circular references in it. What is pagedoc?Meritocracy
What do I mean with what? 1. What is the value of pagedoc? 2. Circular reference: a = {}; a.b = a;Meritocracy
try use node.js : util.inspectLashaunda
i faced this problem and it was made by forgetting await in async function to get values of an function.Scotopia
M
675

It means that the object you pass in the request (I guess it is pagedoc) has a circular reference, something like:

var a = {};
a.b = a;

JSON.stringify cannot convert structures like this.

N.B.: This would be the case with DOM nodes, which have circular references, even if they are not attached to the DOM tree. Each node has an ownerDocument which refers to document in most cases. document has a reference to the DOM tree at least through document.body and document.body.ownerDocument refers back to document again, which is only one of multiple circular references in the DOM tree.

Meritocracy answered 27/1, 2011 at 12:25 Comment(7)
Thanks! This explains the issue I got. But how does the circular reference present in the DOM objects don't cause any issues? Would JSON stringify a document object?Pinta
@asgs: It does cause issues, at least in Chrome. Firefox seems to be a bit smarter about it, but I don't know exactly what it is doing.Meritocracy
Is it possible to "catch" this error and handle it?Dehart
@DougMolineux: Sure, you can use try...catch to catch this error.Meritocracy
@FelixKling Unfortunately I couldn't get that to work (might have been doing something wrong) I ended up using this: github.com/isaacs/json-stringify-safeDehart
" DOM nodes"...oops, forgot a .value, thanks for this, I was looking in all the wrong spotsDeflected
Magnific explanation ✅👍Agc
J
189

As per the JSON docs at Mozilla, JSON.stringify has a second parameter replacer which can be used to filter/ignore children items while parsing the tree. However, perhaps you can avoid the circular references.

In Node.js we cannot. So we can do something like this:

function censor(censor) {
  var i = 0;
  
  return function(key, value) {
    if(i !== 0 && typeof(censor) === 'object' && typeof(value) == 'object' && censor == value) 
      return '[Circular]'; 
    
    if(i >= 29) // seems to be a harded maximum of 30 serialized objects?
      return '[Unknown]';
    
    ++i; // so we know we aren't using the original object anymore
    
    return value;  
  }
}

var b = {foo: {bar: null}};

b.foo.bar = b;

console.log("Censoring: ", b);

console.log("Result: ", JSON.stringify(b, censor(b)));

The result:

Censoring:  { foo: { bar: [Circular] } }
Result: {"foo":{"bar":"[Circular]"}}

Unfortunately there seems to be a maximum of 30 iterations before it automatically assumes it's circular. Otherwise, this should work. I even used areEquivalent from here, but JSON.stringify still throws the exception after 30 iterations. Still, it's good enough to get a decent representation of the object at a top level, if you really need it. Perhaps somebody can improve upon this though? In Node.js for an HTTP request object, I'm getting:

{
"limit": null,
"size": 0,
"chunks": [],
"writable": true,
"readable": false,
"_events": {
    "pipe": [null, null],
    "error": [null]
},
"before": [null],
"after": [],
"response": {
    "output": [],
    "outputEncodings": [],
    "writable": true,
    "_last": false,
    "chunkedEncoding": false,
    "shouldKeepAlive": true,
    "useChunkedEncodingByDefault": true,
    "_hasBody": true,
    "_trailer": "",
    "finished": false,
    "socket": {
        "_handle": {
            "writeQueueSize": 0,
            "socket": "[Unknown]",
            "onread": "[Unknown]"
        },
        "_pendingWriteReqs": "[Unknown]",
        "_flags": "[Unknown]",
        "_connectQueueSize": "[Unknown]",
        "destroyed": "[Unknown]",
        "bytesRead": "[Unknown]",
        "bytesWritten": "[Unknown]",
        "allowHalfOpen": "[Unknown]",
        "writable": "[Unknown]",
        "readable": "[Unknown]",
        "server": "[Unknown]",
        "ondrain": "[Unknown]",
        "_idleTimeout": "[Unknown]",
        "_idleNext": "[Unknown]",
        "_idlePrev": "[Unknown]",
        "_idleStart": "[Unknown]",
        "_events": "[Unknown]",
        "ondata": "[Unknown]",
        "onend": "[Unknown]",
        "_httpMessage": "[Unknown]"
    },
    "connection": "[Unknown]",
    "_events": "[Unknown]",
    "_headers": "[Unknown]",
    "_headerNames": "[Unknown]",
    "_pipeCount": "[Unknown]"
},
"headers": "[Unknown]",
"target": "[Unknown]",
"_pipeCount": "[Unknown]",
"method": "[Unknown]",
"url": "[Unknown]",
"query": "[Unknown]",
"ended": "[Unknown]"
}

I created a small Node.js module to do this here: https://github.com/ericmuyser/stringy Feel free to improve/contribute!

Jinn answered 11/3, 2012 at 7:1 Comment(9)
It's the first time I see a function being passed which returns a self-executing function which returns a regular function. I believe I understand why this was done, but I don't believe I would have found that solution myself, and I feel I could remember this technique better if I could see other examples where this setup is needed. That being said, could you point to any literature regarding this setup/technique (for lack of a better word) or similar ones?Costly
+1 to Shawn. Please remove that IEFE, it's absolutely useless and illegible.Antiquary
thx for pointing out the censor arg! it allows debugging down circular issues. in my case i had a jquery array where i thougth to have a normal array. they both look similar in debug print mode. About the IEFE, I see them frequently used in places where there is absolutely no need for them and agree with Shawn and Bergi that this is just such case.Briannabrianne
Still no solution to the 30 iteration limit?Bename
@Bename #11617130Byng
I'm not sure why, but this solution does not seem to work for me.Tatianna
@BrunoLM: for 30 iterations limit, if you return '[Unknown:' + typeof(value) + ']' you will see how to fix the censor to properly treat functions and some other types.Aeolis
This solution only seems to work for circular references at the object's top level. For anything more deeply nested, not. Check here for a more generic version.Caty
Though if you were actually using node.js you would use util.inspect -- see https://mcmap.net/q/36423/-how-can-i-print-a-circular-structure-in-a-json-like-formatOsculation
H
90

One approach is to strip object and functions from main object. And stringify the simpler form

function simpleStringify (object){
    // stringify an object, avoiding circular structures
    // https://stackoverflow.com/a/31557814
    var simpleObject = {};
    for (var prop in object ){
        if (!object.hasOwnProperty(prop)){
            continue;
        }
        if (typeof(object[prop]) == 'object'){
            continue;
        }
        if (typeof(object[prop]) == 'function'){
            continue;
        }
        simpleObject[prop] = object[prop];
    }
    return JSON.stringify(simpleObject); // returns cleaned up JSON
};

if you are using node js use inspect()

import {inspect} from "util";
console.log(inspect(object));
Hankins answered 22/7, 2015 at 8:28 Comment(6)
Perfect answer for me. Maybe 'function' keyword missed?Alembic
Nice, thanks. Though, this leads to strip also unrelated object props available in the main object. If that's the case, the C.M.'s answer is a good alternative.Mummery
thank you mate, you just saved me hours and hours of searching with this. can't express it enough.Blynn
inspect works perfect, no more headache, thank you!Tellus
You seem to insist on the spelling insepect in your code, which looks like a typo. Do I miss something?Auditorium
WARNING: The util.inspect() method returns a string representation of object that is intended for debugging. The output of util.inspect may change at any time and should not be depended upon programmatically.Belch
C
53

I normally use the circular-json npm package to solve this.

// Felix Kling's example
var a = {};
a.b = a;
// load circular-json module
var CircularJSON = require('circular-json');
console.log(CircularJSON.stringify(a));
//result
{"b":"~"}

Note: circular-json has been deprecated, I now use flatted (from the creator of CircularJSON):

// ESM
import {parse, stringify} from 'flatted/esm';

// CJS
const {parse, stringify} = require('flatted/cjs');

const a = [{}];
a[0].a = a;
a.push(a);

stringify(a); // [["1","0"],{"a":"0"}]

from: https://www.npmjs.com/package/flatted

Craftsman answered 22/3, 2017 at 11:38 Comment(2)
tnx!. the import syntax little bit changed. see here github.com/WebReflection/flatted#readmeSouter
This package has been deprecatedLovell
K
30

Based on zainengineer's answer... Another approach is to make a deep copy of the object and strip circular references and stringify the result.

function cleanStringify(object) {
    if (object && typeof object === 'object') {
        object = copyWithoutCircularReferences([object], object);
    }
    return JSON.stringify(object);

    function copyWithoutCircularReferences(references, object) {
        var cleanObject = {};
        Object.keys(object).forEach(function(key) {
            var value = object[key];
            if (value && typeof value === 'object') {
                if (references.indexOf(value) < 0) {
                    references.push(value);
                    cleanObject[key] = copyWithoutCircularReferences(references, value);
                    references.pop();
                } else {
                    cleanObject[key] = '###_Circular_###';
                }
            } else if (typeof value !== 'function') {
                cleanObject[key] = value;
            }
        });
        return cleanObject;
    }
}

// Example

var a = {
    name: "a"
};

var b = {
    name: "b"
};

b.a = a;
a.b = b;

console.log(cleanStringify(a));
console.log(cleanStringify(b));
Koa answered 17/2, 2018 at 19:43 Comment(2)
This was the best way to solve it, thxBarrens
I wish this were the accepted answer. This is the one answer that actually allowed me to debug WHY I have a circular reference in a super-complex object structure. TYVMFebruary
S
8

In my case I simply forgot to use async/await thing while building the route:

app.get('/products', async (req, res) => {
    const products = await Product.find();
    res.send(products );
});
Salivate answered 7/7, 2021 at 19:21 Comment(1)
lol was my case tooCyrille
L
6

I resolve this problem on NodeJS like this:

var util = require('util');

// Our circular object
var obj = {foo: {bar: null}, a:{a:{a:{a:{a:{a:{a:{hi: 'Yo!'}}}}}}}};
obj.foo.bar = obj;

// Generate almost valid JS object definition code (typeof string)
var str = util.inspect(b, {depth: null});

// Fix code to the valid state (in this example it is not required, but my object was huge and complex, and I needed this for my case)
str = str
    .replace(/<Buffer[ \w\.]+>/ig, '"buffer"')
    .replace(/\[Function]/ig, 'function(){}')
    .replace(/\[Circular]/ig, '"Circular"')
    .replace(/\{ \[Function: ([\w]+)]/ig, '{ $1: function $1 () {},')
    .replace(/\[Function: ([\w]+)]/ig, 'function $1(){}')
    .replace(/(\w+): ([\w :]+GMT\+[\w \(\)]+),/ig, '$1: new Date("$2"),')
    .replace(/(\S+): ,/ig, '$1: null,');

// Create function to eval stringifyed code
var foo = new Function('return ' + str + ';');

// And have fun
console.log(JSON.stringify(foo(), null, 4));
Lordan answered 26/2, 2016 at 9:20 Comment(0)
K
5

For my case I was getting that error when I was using async function on my server-side to fetch documents using mongoose. It turned out that the reason was I forgot to put await before calling find({}) method. Adding that part fixed my issue.

Kafiristan answered 8/1, 2020 at 15:42 Comment(1)
friendly reminder: As of jQuery 1.8 the await/async is deprecated - so dont use it.Rhodium
C
5

This works and tells you which properties are circular. It also allows for reconstructing the object with the references

  JSON.stringifyWithCircularRefs = (function() {
    const refs = new Map();
    const parents = [];
    const path = ["this"];

    function clear() {
      refs.clear();
      parents.length = 0;
      path.length = 1;
    }

    function updateParents(key, value) {
      var idx = parents.length - 1;
      var prev = parents[idx];
      if (prev[key] === value || idx === 0) {
        path.push(key);
        parents.push(value);
      } else {
        while (idx-- >= 0) {
          prev = parents[idx];
          if (prev[key] === value) {
            idx += 2;
            parents.length = idx;
            path.length = idx;
            --idx;
            parents[idx] = value;
            path[idx] = key;
            break;
          }
        }
      }
    }

    function checkCircular(key, value) {
      if (value != null) {
        if (typeof value === "object") {
          if (key) { updateParents(key, value); }

          let other = refs.get(value);
          if (other) {
            return '[Circular Reference]' + other;
          } else {
            refs.set(value, path.join('.'));
          }
        }
      }
      return value;
    }

    return function stringifyWithCircularRefs(obj, space) {
      try {
        parents.push(obj);
        return JSON.stringify(obj, checkCircular, space);
      } finally {
        clear();
      }
    }
  })();

Example with a lot of the noise removed:

{
    "requestStartTime": "2020-05-22...",
    "ws": {
        "_events": {},
        "readyState": 2,
        "_closeTimer": {
            "_idleTimeout": 30000,
            "_idlePrev": {
                "_idleNext": "[Circular Reference]this.ws._closeTimer",
                "_idlePrev": "[Circular Reference]this.ws._closeTimer",
                "expiry": 33764,
                "id": -9007199254740987,
                "msecs": 30000,
                "priorityQueuePosition": 2
            },
            "_idleNext": "[Circular Reference]this.ws._closeTimer._idlePrev",
            "_idleStart": 3764,
            "_destroyed": false
        },
        "_closeCode": 1006,
        "_extensions": {},
        "_receiver": {
            "_binaryType": "nodebuffer",
            "_extensions": "[Circular Reference]this.ws._extensions",
        },
        "_sender": {
            "_extensions": "[Circular Reference]this.ws._extensions",
            "_socket": {
                "_tlsOptions": {
                    "pipe": false,
                    "secureContext": {
                        "context": {},
                        "singleUse": true
                    },
                },
                "ssl": {
                    "_parent": {
                        "reading": true
                    },
                    "_secureContext": "[Circular Reference]this.ws._sender._socket._tlsOptions.secureContext",
                    "reading": true
                }
            },
            "_firstFragment": true,
            "_compress": false,
            "_bufferedBytes": 0,
            "_deflating": false,
            "_queue": []
        },
        "_socket": "[Circular Reference]this.ws._sender._socket"
    }
}

To reconstruct call JSON.parse() then loop through the properties looking for the [Circular Reference] tag. Then chop that off and... eval... it with this set to the root object.

Don't eval anything that can be hacked. Better practice would be to do string.split('.') then lookup the properties by name to set the reference.

Cason answered 22/5, 2020 at 19:59 Comment(1)
Got VM1226:21 Uncaught TypeError: Cannot read properties of undefined (reading 'ancestorOrigins') on stringifyin window on the web page I need.Benedict
S
3

In my case I am using React Native, and tried to debug

console.log(JSON.stringify(object))

and got the error:

TypeError: Converting circular structure to JSON

It seems that I can get the object logged to the console by using just plain:

console.log(object)
Soup answered 28/5, 2021 at 3:39 Comment(2)
this is the same problem im having. any solutions/ideas?Personage
Try it both ways: console.log(object) and console.log(JSON.stringify(object)) if there is a difference. In my case there was.Soup
R
3

I got into a different issue here, I was taking values from html elements into an object array, in one field i was assigning values incorrectly which was causing this exception. Incorrect expression: obj.firstname=$("txFirstName") Correct expression: obj.firstname=$("txFirstName").val()

Riemann answered 30/9, 2021 at 5:37 Comment(1)
This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From ReviewTartaric
S
2

I have experienced the same error when trying to build the message below with jQuery. The circular reference happens when reviewerName was being mistakenly assigned to msg.detail.reviewerName. JQuery's .val() fixed the issue, see last line.

var reviewerName = $('reviewerName'); // <input type="text" id="taskName" />;
var msg = {"type":"A", "detail":{"managerReview":true} };
msg.detail.reviewerName = reviewerName; // Error
msg.detail.reviewerName = reviewerName.val(); // Fixed
Simpatico answered 2/2, 2012 at 23:12 Comment(0)
V
2

In my case it was a flush() that was left over in the unit test after some code changes.

Before

it('something should be...', () => {
// do tests
flush();
}

After

it('something should be...', () => {
// do tests
}
Vinous answered 16/11, 2021 at 22:10 Comment(0)
F
0

I was getting the same error with jQuery formvaliadator, but when I removed a console.log inside success: function, it worked.

Frostbite answered 2/5, 2017 at 3:59 Comment(0)
P
0

You might have done something like this

<Button onClick={fetchSuggestions}>

failing to realize you have passed 'event object' to that function

if you don't wish to pass anything simply send an empty string

<Button onClick={() => fetchSuggestions()}>
  const fetchSuggestions = async (propsSession) => {
    const {
      error,
      hasNextDoc,
      suggestions: moreSuggestions,
    } = await fetcher(`/admin/fetchSuggestion`, {
      initialRequest: !!propsSession,
      session: propsSession || session,
    });
  }
Phenanthrene answered 29/5, 2022 at 14:9 Comment(0)
S
0

For me there was a jquery or HTML element in the data object as property "target" which contains a circular reference. The property was not needed for the purposes of the data object being sent as JSON.

Removing it by deleting the property fixed the issue:

if(__dataObj.hasOwnProperty('target')){
    // remove target from data to avoid circular structure error
    delete __dataObj.target;
}
Suppletion answered 15/2, 2023 at 19:59 Comment(0)
K
-1

Node.js v10.22.1 (the version running on our GitLab CI server) has, what I consider to be, an erroneous circular reference detector. The version running locally (v12.8.0) is smart enough to know it's not a true circular reference.

I'm adding this response in case someone else has the same issue and their object isn't actually a circular reference.

This was the original response object:

var res = {
    "status":"OK",
    "message":"Success",
    "errCode":":",
    "data":"",
    "appCfg":{
        "acp_age":"2yy",
        "acp_us":"yes",
        "mode":"admin",
        "version":"v1.21.07.1"
    },
    "reqID":59833,
    "email":{
        "status":"OK",
        "message":"Success"
    },
    "emailStatus":"sent"
}

It thought that res.email.status was the same as res.status. It's just a text element, so not circular, but the name and value apparently tripped up the JSON.stringify parser.

I removed the res.email sub-object and everything is fine. I was trying to collect independent statuses and detailed messages from all of the unique actions performed during the server call. I've switched it to the element res.emailStatus which is also included in the example above.

Kyrstin answered 26/5, 2021 at 15:4 Comment(0)
G
-1

As per MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#issue_with_json.stringify_when_serializing_circular_references

It is a circular json, which cannot be directly converted.

Solution 1:

https://www.npmjs.com/package/flatted

// ESM
import {parse, stringify, toJSON, fromJSON} from 'flatted';

// CJS
const {parse, stringify, toJSON, fromJSON} = require('flatted');

const a = [{}];
a[0].a = a;
a.push(a);

stringify(a); // [["1","0"],{"a":"0"}]

Solution 2: (Also by MDN)

https://github.com/douglascrockford/JSON-js

Goldsberry answered 12/6, 2022 at 13:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.