karatelabs/karate · error · JsErrorException

Converting circular structure to JSON

Error message

Converting circular structure to JSON

What it means

JSON.stringify detected a circular reference: the same Map/List (object/array) instance appeared twice on the current serialization path (tracked via the `seen` identity set). Since the structure would never terminate, the serializer throws a TypeError('Converting circular structure to JSON').

Solutions

  1. Break the cycle before serializing (remove/null out back-references).
  2. Use a replacer that skips known circular keys: `(k, v) => k === 'parent' ? undefined : v`.
  3. Serialize a projected copy containing only the fields you need.
  4. If cycles are intentional, switch to a format that supports them or serialize node ids instead of object references.

Example fix

// before
node.parent = tree;
karate.stringify(tree); // cycle
// after
var json = karate.stringify(tree, function(k, v) {
  return k === 'parent' ? undefined : v;
});
Defensive patterns

Strategy: validation

Validate before calling

function hasCycle(root) { var seen = new Set(); function walk(v) { if (!(v && typeof v === 'object')) return false; if (seen.has(v)) return true; seen.add(v); return Object.values(v).some(walk); } return walk(root); }

Try / catch

try { json = karate.stringify(obj); } catch (e) { if (String(e).indexOf('circular') !== -1) { json = karate.stringify(obj, function(k, v) { return k === 'parent' ? undefined : v; }); } else { throw e; } }

Prevention

When it happens

Trigger: Serializing objects that reference themselves directly or transitively — e.g. obj.self = obj, parent/child doubly-linked nodes, or JS wrappers holding Java maps that point back.

Common situations: Caching a parent reference on child objects then stringifying the tree; attaching a request object to its own response for debugging; graph-shaped data (linked lists, DOM-like nodes) dumped to JSON.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/8e6009fc10d1dacf. Report an issue: GitHub.

Appendix: source

Thrown at karate-js/src/main/java/io/karatelabs/js/JsJson.java:187

            return Terms.UNDEFINED;
        }
        if (value instanceof JsValue jv && !(value instanceof JsUndefined)) {
            value = jv.getJavaValue(); // Number / String / Boolean / Date wrappers
        }
        if (value instanceof BigInteger || value instanceof JsBigInt) {
            throw JsErrorException.typeError("Do not know how to serialize a BigInt");
        }
        // §25.5.2.2 SerializeJSONNumber: a finite Number is its ToString, a
        // non-finite one is the literal null — JSON has no NaN / Infinity.
        // Only here: Terms.numberToString stays the ToString seam String(NaN) shares.
        if (value instanceof Number n && !Double.isFinite(n.doubleValue())) {
            return null;
        }
        if (!(value instanceof Map<?, ?>) && !(value instanceof List<?>)) {
            return value;
        }
        if (!seen.add(value)) {
            throw JsErrorException.typeError("Converting circular structure to JSON");
        }
        try {
            return value instanceof List<?> list
                    ? serializeArray(context, list, replacerFn, propertyList, seen)
                    : serializeObject(context, value, replacerFn, propertyList, seen);
        } finally {
            seen.remove(value);
        }
    }

    private static Object serializeObject(Context context, Object value, JsCallable replacerFn,
                                          List<String> propertyList, Set<Object> seen) {
        Map<Object, Object> result;
        // a PropertyList always reshapes the object — it selects and reorders keys
        boolean changed = propertyList != null;
        if (value instanceof JsObject jo) {
            // §25.5.2 SerializeJSONProperty does Get(holder, key): accessor
            // getters run, and a PropertyList key resolves through the

View on GitHub (pinned to a22eb90246)