JSON Circular Reference Error – Cause and Fix

If you call JSON.stringify() on an object and get TypeError: Converting circular structure to JSON, the object contains a reference loop — somewhere in the data, an object points back to itself. JSON is a tree format; it cannot represent cycles.

What a circular reference looks like

const parent = { name: "parent" };
const child  = { name: "child" };

parent.child = child;
child.parent = parent;  // circular: child points back to parent

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 circle

Common sources of circular references

Fix 1 — remove the back-reference before serializing

The cleanest solution is to not include the circular property in the output at all:

const safeChild = { name: child.name }; // copy only what you need
JSON.stringify({ name: parent.name, child: safeChild }); // works

Fix 2 — use a replacer function to skip seen objects

If you do not know in advance which property is circular, use a replacer that tracks visited objects and replaces any back-reference with a placeholder:

function circularReplacer() {
    const seen = new WeakSet();
    return (key, value) => {
        if (typeof value === 'object' && value !== null) {
            if (seen.has(value)) return '[Circular]';
            seen.add(value);
        }
        return value;
    };
}

JSON.stringify(parent, circularReplacer());
// '{"name":"parent","child":{"name":"child","parent":"[Circular]"}}'

Fix 3 — use a library for full circular serialization

If you need to serialize and then deserialize circular structures (not just log them), use the flatted npm package, which encodes cycles as index references:

import { stringify, parse } from 'flatted';

const json = stringify(parent);   // encodes cycles
const obj  = parse(json);         // restores the circular structure

Note that the output format is not standard JSON — you can only parse it back with flatted.

Serializing only the data you need

In practice, the best fix for API responses is to map your internal objects to plain data transfer objects (DTOs) before serialization. This prevents accidental exposure of internal references and circular structures:

// Internal model
const user = orm.findUser(id); // has circular ORM references

// Safe DTO for the API response
const dto = {
  id: user.id,
  name: user.name,
  email: user.email
};

res.json(dto); // never pass the ORM model directly

Why the error happens at serialization, not creation

A circular reference is perfectly legal in memory — objects pointing at each other is how linked lists, trees with parent pointers, and DOM nodes work. The problem only appears when you try to flatten that graph into text. JSON.stringify walks the object depth-first, and when it revisits a node it has already seen, it can't represent "the same object again" in a linear format, so it throws TypeError: Converting circular structure to JSON. Node.js even tells you the property path that closed the loop, which is the fastest way to find the offending back-reference. Understanding this is the key to the fix: you're not removing data the program needs, you're only deciding how to represent a shared reference when the structure is serialized.

Circular references and structuredClone

It's worth knowing that not every "deep copy" operation chokes on cycles the way JSON does. The modern structuredClone() function handles circular references natively, so if your goal is to duplicate an object in memory rather than send it over the wire, reach for that instead of the old JSON.parse(JSON.stringify(obj)) trick — the round-trip approach not only fails on cycles, it also silently drops functions, undefined, and Date objects turn into strings. Serialization for transport is a different job: there you genuinely have to break the cycle, either by removing the back-reference before stringifying or by replacing it with an ID that the receiver can re-link. When you need to confirm the result is clean, valid JSON, paste it into the JSON Validator.

Try it directly in your browser — free, no signup:

Open JSON Validator →
About the author

Pasindu Ishan is a software developer based in Sri Lanka. He builds developer tools at JSON Dev Tools.