Uncaught TypeError: Converting circular structure to JSON (cyclic object value in JSON.stringify)
Overview
The error “Uncaught TypeError: Converting circular structure to JSON” occurs when attempting to use JSON.stringify()
on an object that has circular references. A circular reference means that an object contains a reference to itself, either directly or indirectly, creating an infinite loop when trying to convert it to a JSON string.
Understanding JSON.stringify()
JSON.stringify()
is a built-in JavaScript method used to convert a JavaScript object into a JSON string. However, JavaScript’s JSON.stringify()
does not support circular references because JSON is a tree-structured format, not a graph.
Example of JSON.stringify() in a Simple Object
const obj = {
name: "John",
age: 30
};
console.log(JSON.stringify(obj));
// Output: {"name":"John","age":30}
This works fine because obj
does not have any cyclic references.
What Causes the Circular Structure Error?
A circular reference happens when an object refers to itself within one of its properties, either directly or through a nested structure.
Example of a Circular Reference
let obj = {};
obj.self = obj; // obj contains a reference to itself
console.log(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSON
In this example, obj
contains a property self
that refers back to obj
itself. When JSON.stringify(obj)
is called, it goes into an infinite loop because it keeps trying to serialize the self
property, which leads back to the object.
Example with an Indirect Circular Reference
let obj1 = {};
let obj2 = { parent: obj1 };
obj1.child = obj2;
console.log(JSON.stringify(obj1));
// Uncaught TypeError: Converting circular structure to JSON
Here, obj1
has a child
property referring to obj2
, while obj2
has a parent
property referring back to obj1
. This indirect circular reference also results in an error when calling JSON.stringify()
.
How to Handle Circular References in JSON.stringify()
1. Removing Circular References Manually
If you have control over the object, you can remove circular references before calling JSON.stringify()
.
let obj = {};
obj.name = "Alice";
obj.self = obj; // Circular reference
delete obj.self; // Remove the circular reference
console.log(JSON.stringify(obj)); // {"name":"Alice"}
In this case, manually deleting the reference allows JSON.stringify()
to work correctly.
2. Using a Custom Serializer with a Seen Set
You can use a custom replacer function to track seen objects and prevent circular references from being serialized.
function removeCircularReferences() {
const seen = new WeakSet();
return function (key, value) {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return "[Circular]"; // Replace circular references with a placeholder
}
seen.add(value);
}
return value;
};
}
let obj = {};
obj.self = obj; // Circular reference
console.log(JSON.stringify(obj, removeCircularReferences()));
// Output: {"self":"[Circular]"}
Here, a WeakSet
is used to track visited objects. If an object is encountered again, it replaces it with a "[Circular]"
placeholder.
3. Using Third-Party Libraries
Several third-party libraries can handle circular references automatically:
Using flatted
(npm package)
Flatted is a lightweight JSON parser that supports circular structures.
npm install flatted
import { stringify, parse } from "flatted";
let obj = {};
obj.self = obj; // Circular reference
console.log(stringify(obj));
// Output: {"self":"~"}
let parsed = parse(stringify(obj));
console.log(parsed.self === parsed); // true (restores the original reference)
Using circular-json
npm install circular-json
const CircularJSON = require("circular-json");
let obj = {};
obj.self = obj; // Circular reference
console.log(CircularJSON.stringify(obj));
// Output: {"self":"[Circular]"}
This library safely serializes circular references and reconstructs them when parsing.
4. Cloning Objects Without Circular References
If you need to clone an object but avoid circular references:
function deepClone(obj, seen = new WeakMap()) {
if (typeof obj !== "object" || obj === null) {
return obj;
}
if (seen.has(obj)) {
return "[Circular]";
}
let copy = Array.isArray(obj) ? [] : {};
seen.set(obj, copy);
for (let key in obj) {
copy[key] = deepClone(obj[key], seen);
}
return copy;
}
let obj = {};
obj.self = obj; // Circular reference
let cloned = deepClone(obj);
console.log(JSON.stringify(cloned));
// Output: {"self":"[Circular]"}
This function recursively clones objects while avoiding circular references.
Common Use Cases Where This Error Occurs
- Handling DOM elements with references
let div = document.createElement("div"); div.ref = div; // Circular reference console.log(JSON.stringify(div)); // Uncaught TypeError: Converting circular structure to JSON
- Logging Circular References in Debugging
- If you try
console.log(JSON.stringify(window))
, you’ll get the same error because thewindow
object has circular references.
- If you try
- Working with Data Structures like Trees and Graphs
- Trees and graphs often have parent-child references, leading to circular references.
- Serializing Large Objects with Cross-References
let user = { name: "Alice" }; let order = { id: 123, customer: user }; user.lastOrder = order; console.log(JSON.stringify(user)); // Uncaught TypeError: Converting circular structure to JSON