Understanding The Error ‘Converting circular structure to JSON’

What is it?

Sometimes, we use JSON.stringify method to convert an Array or Object to a standard JSON string. Yes, It may occur when you do that convert. It’s a JavaScript error. This is one of the most common errors.


Why does it happen?

As we know, ES6 has 6 data types that are passed by value: String, Boolean, Null, Undefined, Number, Symbol. These are primitive types. Others data types are passed by reference. We know the reference is memory address or pointer. Why does it happen is that the Object that is converted has a circular pointer. The JSON.stringify enter an infinite loop and JavaScript engine give an error. There is a variable named menuConfig below.

let menuConfig = {
    label: 'Products',
}
menuConfig.children = [
    {
        label: 'product_1',
        parent: menuConfig, // parent attribute point to menuConfig
    },
    {
        label: 'product_2',
        parent: menuConfig,
    },
    // ...
];

The variable menuConfig like a doubly linked list. We can access the item's children by its own attribute named children. Also, we can access the item’s parent by its own attribute named parent.

You will see when you run the snippet's code on Node.



You will see the screenshot below when you run the snippet's code on Chrome Devtool

As we see, the attribute named parent refers to the variable menuConfig.


Javascript engine stringify menuConfig.children is the same as below code

function json2Str(parm1) {
  if (Array.isArray(parm1)) {
    parm1.forEach((item) => {
      if (item && item.parent) {
        json2Str(item.parent.children);
      } else {
        // to do other thing
      }
    })
  } else 
    // to do other thing
  }
}
json2Str(menuConfig.children);

It's a recurive function and will never stop and will get an error that is 'Maximum call stack size exceeded'. That's why JavaScript engine gives you an error when you convert a circular reference object to JSON string.


How to solve it?

You could transfer the object to another object and except or delete circular attributes. Just copy un-circular attributes. For example:

let anotherMenuConfig = menuConfig.map((menu) => {
   return {
       label: menu.label,
       // no parent attribute
       // other attrbute
   };
});
JSON.stringify(anotherMenuConfig);
// or
menuConfig.forEach((item) => {
    delete item.parent;
});
JSON.stringify(menuConfig);


The ultimate solution

I released a open tool that can check circular reference and print its attribute chain. Here is the link: check-circular-reference


Summary

You know what’s going on the error ‘Converting circular structure to JSON’.
This article helps you understand the circular reference structure. This makes me think another question that how to check an object existing circular reference.


This article is original, licensed under a Creative Commons Attribution 4.0 International license