Chrome sendrequest error: TypeError: Converting circular structure to JSON

JavascriptJsonGoogle ChromeGoogle Chrome-Extension

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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!

Solution 3 - Javascript

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

function simpleStringify (object){
    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
};

Solution 4 - Javascript

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

Solution 5 - Javascript

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));

Solution 6 - Javascript

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));

Solution 7 - Javascript

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.

Solution 8 - Javascript

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.

Solution 9 - Javascript

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 );
});

Solution 10 - Javascript

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()

Solution 11 - Javascript

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

Solution 12 - Javascript

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
}

Solution 13 - Javascript

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)

Solution 14 - Javascript

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

Solution 15 - Javascript

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSkizitView Question on Stackoverflow
Solution 1 - JavascriptFelix KlingView Answer on Stackoverflow
Solution 2 - JavascriptEric MuyserView Answer on Stackoverflow
Solution 3 - JavascriptzainengineerView Answer on Stackoverflow
Solution 4 - Javascriptuser3139574View Answer on Stackoverflow
Solution 5 - JavascriptC.M.View Answer on Stackoverflow
Solution 6 - JavascriptMiFView Answer on Stackoverflow
Solution 7 - JavascriptMussa CharlesView Answer on Stackoverflow
Solution 8 - JavascriptDerek ZiembaView Answer on Stackoverflow
Solution 9 - JavascriptArtemNovikovView Answer on Stackoverflow
Solution 10 - JavascriptAltafView Answer on Stackoverflow
Solution 11 - JavascriptizilottiView Answer on Stackoverflow
Solution 12 - JavascriptJulio Cesar Brito GomesView Answer on Stackoverflow
Solution 13 - JavascriptPHZ.fi-PharazonView Answer on Stackoverflow
Solution 14 - JavascriptAzmeerView Answer on Stackoverflow
Solution 15 - JavascriptLauraView Answer on Stackoverflow