JSON troubleshooting
TypeError: Converting circular structure to JSON
Read the V8 circular path, find the property that closes the loop and serialize plain data instead. Covers Firefox, Safari, Python, Go, .NET and Jackson.
What the error means
JSON can only describe trees, where each object or array sits inside one parent. JSON.stringify met an object that refers back to itself or to an object it is still serializing, so the output would never end. V8 prints the loop as a path: the --> line is where the cycle starts, each | line is one step, and the --- line names the property that points back. Typical sources are parent links in trees, framework objects such as HTTP requests and sockets, DOM nodes, and ORM entities with two-way relations.
Exact messages by runtime
Messages depend on the input and runtime version. Node and Python rows were reproduced locally. Other rows cite official documentation or source. Rows marked as fragments or templates preserve the source wording and are not complete exception transcripts. Browsers and libraries may append location details.
| Runtime | Message | Input and evidence |
|---|---|---|
| JavaScript / V8 | TypeError: Converting circular structure to JSON | const a = {}; a.self = a; JSON.stringify(a)Reproduced in Node.js 24.21.0; V8 adds the path lines shown in the examples |
| Firefox | TypeError: cyclic object value | The same inputOfficial SpiderMonkey message, Firefox 158 source |
| Safari / JavaScriptCore | TypeError: JSON.stringify cannot serialize cyclic structures. | The same inputJavaScriptCore source (WebKit tag webkitgtk-2.54.1) |
| Python | ValueError: Circular reference detected | a = {}; a['self'] = a; json.dumps(a)Reproduced in Python 3.14.4 |
| Python 3.14+, note | when serializing dict item 'self' | Printed under the ValueErrorReproduced in Python 3.14.4; notes were added in CPython 3.14 |
| Go / encoding/json | json: unsupported value: encountered a cycle via *main.Node | json.Marshal of a *Node whose Next field points back to itSource-derived from Go 1.27 encode.go; Go starts checking after 1000 nested pointers |
| C# / System.Text.Json | A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 64. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. | Two objects that reference each otherOfficial .NET 10 resource string with the default depth of 64; path omitted |
| C# / Json.NET | Self referencing loop detected for property '{0}' with type '{1}'. | Source templateJson.NET 13.0.4 source template; path suffix omitted |
| Java / Jackson | Infinite recursion (StackOverflowError) | Two entities that reference each otherOfficial Jackson 2.22 source text; the reference chain follows |
Causes you can reproduce
A child keeps a reference to its parent
Trees with parent links, linked lists and graphs all contain cycles. The --- line names the back-reference, here the parent property of the child.
const parent = { name: 'root' };
const child = { name: 'leaf', parent };
parent.child = child;
JSON.stringify(parent);
// TypeError: Converting circular structure to JSON
// --> starting at object with constructor 'Object'
// | property 'child' -> object with constructor 'Object'
// --- property 'parent' closes the circleA Node.js HTTP object was serialized
Requests, responses, sockets and HTTP client errors hold references to each other. Passing one to JSON.stringify, to res.json or to a logger that stringifies prints a path through ClientRequest, Socket or HTTPParser. Serialize the fields you need instead, such as the status code and the body.
import http from 'node:http';
const request = http.get('http://example.com/');
request.on('error', () => {});
JSON.stringify(request);
// TypeError: Converting circular structure to JSON
// --> starting at object with constructor 'ClientRequest'
// | property 'agent' -> object with constructor 'Agent'
// | property 'sockets' -> object with constructor 'Object'
// | ...
// | index 0 -> object with constructor 'Socket'
// --- property '_httpMessage' closes the circleTwo records reference each other
An ORM or a hand-built model often links a customer to orders and each order back to the customer. Python 3.14 lists the path in notes, from the inner value outward.
import json
customer = {"name": "Ada"}
order = {"id": 7, "customer": customer}
customer["orders"] = [order]
json.dumps(customer)
# ValueError: Circular reference detected
# when serializing dict item 'customer'
# when serializing list item 0
# when serializing dict item 'orders'How to fix it
- Read the path. In V8 the --- line names the property that points back. In Python 3.14 the notes list the path from the inner value outward.
- Decide what the receiver needs from that reference. Usually an ID is enough, not the whole parent object.
- Serialize plain data built for output: pick the fields, replace back-references with IDs, or give the class a toJSON method that does this.
- For logs, use a logger or util.inspect, which marks cycles as [Circular *1] instead of throwing.
Send IDs instead of back-references
An explicit output shape also keeps internal fields out of the JSON.
const parent = { id: 1, name: 'root', children: [] };
const child = { id: 2, name: 'leaf', parent };
parent.children.push(child);
const text = JSON.stringify({
id: parent.id,
name: parent.name,
children: parent.children.map(c => ({ id: c.id, name: c.name, parentId: c.parent.id })),
});
console.log(text);
// {"id":1,"name":"root","children":[{"id":2,"name":"leaf","parentId":1}]}Replace only real cycles with a replacer
Use this for diagnostics or when the receiver accepts a placeholder. Unlike a WeakSet of every object seen, it keeps an object that two branches share and replaces only references back to an ancestor. It must be a function expression because it reads this.
function withoutCycles() {
const ancestors = [];
return function (key, value) {
if (typeof value !== 'object' || value === null) return value;
// `this` is the object that holds `key`; drop entries that are not its ancestors
while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop();
if (ancestors.includes(value)) return '[Circular]';
ancestors.push(value);
return value;
};
}
const shared = { label: 'shared' };
const node = { a: shared, b: shared };
node.self = node;
console.log(JSON.stringify(node, withoutCycles()));
// {"a":{"label":"shared"},"b":{"label":"shared"},"self":"[Circular]"}Build a Python payload with IDs
Choose the fields to send and refer to related records by ID.
import json
customer = {"id": 1, "name": "Ada"}
order = {"id": 7, "customer": customer}
customer["orders"] = [order]
payload = {
"id": customer["id"],
"name": customer["name"],
"orders": [{"id": o["id"], "customer_id": o["customer"]["id"]} for o in customer["orders"]],
}
print(json.dumps(payload))
# {"id": 1, "name": "Ada", "orders": [{"id": 7, "customer_id": 1}]}Frequently asked questions
What does closes the circle mean?
The property on the --- line points back to an object that is already on the path being serialized. Leaving out or replacing that one reference in the output breaks the loop.
Why does the error mention ClientRequest, Socket or HTTPParser?
A Node.js HTTP object reached JSON.stringify, directly or through res.json, a logger or an HTTP client error that holds the request. Serialize only the data you need, such as the status code and the response body.
Is a WeakSet replacer safe?
A replacer that drops every object it has seen before also removes shared objects that are not circular, so data disappears silently. Track only the ancestors of the current value, as the replacer above does.
How do I fix it in .NET or Java?
System.Text.Json: set ReferenceHandler to IgnoreCycles or Preserve. Json.NET: use ReferenceLoopHandling.Ignore. Jackson: use @JsonManagedReference and @JsonBackReference, or @JsonIgnore on the back-reference. A DTO without back-references works everywhere.
Related errors and tools
- TypeError: Do not know how to serialize a BigInt
- TypeError: Object of type … is not JSON serializable
- "undefined" or "[object Object]" is not valid JSON
- All JSON error guides
- JSON syntax reference
Standards and diagnostic sources
RFC 8259 and ECMA-404 define JSON syntax. Diagnostic wording belongs to the parser. The table links each cited message to its source.