{"record":{"id":"8e6009fc10d1dacf","repo":"karatelabs/karate","slug":"converting-circular-structure-to-json","errorCode":null,"errorMessage":"Converting circular structure to JSON","messagePattern":"Converting circular structure to JSON","errorType":"exception","errorClass":"JsErrorException","httpStatus":null,"severity":"error","filePath":"karate-js/src/main/java/io/karatelabs/js/JsJson.java","lineNumber":187,"sourceCode":"            return Terms.UNDEFINED;\n        }\n        if (value instanceof JsValue jv && !(value instanceof JsUndefined)) {\n            value = jv.getJavaValue(); // Number / String / Boolean / Date wrappers\n        }\n        if (value instanceof BigInteger || value instanceof JsBigInt) {\n            throw JsErrorException.typeError(\"Do not know how to serialize a BigInt\");\n        }\n        // §25.5.2.2 SerializeJSONNumber: a finite Number is its ToString, a\n        // non-finite one is the literal null — JSON has no NaN / Infinity.\n        // Only here: Terms.numberToString stays the ToString seam String(NaN) shares.\n        if (value instanceof Number n && !Double.isFinite(n.doubleValue())) {\n            return null;\n        }\n        if (!(value instanceof Map<?, ?>) && !(value instanceof List<?>)) {\n            return value;\n        }\n        if (!seen.add(value)) {\n            throw JsErrorException.typeError(\"Converting circular structure to JSON\");\n        }\n        try {\n            return value instanceof List<?> list\n                    ? serializeArray(context, list, replacerFn, propertyList, seen)\n                    : serializeObject(context, value, replacerFn, propertyList, seen);\n        } finally {\n            seen.remove(value);\n        }\n    }\n\n    private static Object serializeObject(Context context, Object value, JsCallable replacerFn,\n                                          List<String> propertyList, Set<Object> seen) {\n        Map<Object, Object> result;\n        // a PropertyList always reshapes the object — it selects and reorders keys\n        boolean changed = propertyList != null;\n        if (value instanceof JsObject jo) {\n            // §25.5.2 SerializeJSONProperty does Get(holder, key): accessor\n            // getters run, and a PropertyList key resolves through the","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-js/src/main/java/io/karatelabs/js/JsJson.java#L169-L205","documentation":"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').","triggerScenarios":"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.","commonSituations":"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.","solutions":["Break the cycle before serializing (remove/null out back-references).","Use a replacer that skips known circular keys: `(k, v) => k === 'parent' ? undefined : v`.","Serialize a projected copy containing only the fields you need.","If cycles are intentional, switch to a format that supports them or serialize node ids instead of object references."],"exampleFix":"// before\nnode.parent = tree;\nkarate.stringify(tree); // cycle\n// after\nvar json = karate.stringify(tree, function(k, v) {\n  return k === 'parent' ? undefined : v;\n});","handlingStrategy":"validation","validationCode":"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); }","typeGuard":null,"tryCatchPattern":"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; } }","preventionTips":["Avoid back-references (parent pointers) in objects destined for JSON","Project to plain DTOs before serializing graph-shaped data","Use a replacer to drop known cyclic keys","Keep debug attachments out of serialized payloads"],"tags":["javascript","json","circular-reference","serialization"],"backgroundTag":"json-serialization-failed","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}