ValueError: Circular reference detected in Python [Solved]

avatar
Borislav Hadzhiev

Last updated: Apr 13, 2024
3 min

banner

# Table of Contents

  1. ValueError: Circular reference detected in Python
  2. Using the dict.copy() method to solve the error
  3. Removing the circular reference before calling json.dumps()
  4. Removing circular refs with a reusable function
  5. Using the default argument of json.dumps() incorrectly

# ValueError: Circular reference detected in Python [Solved]

The Python "ValueError: Circular reference detected" occurs when you try to convert a dictionary that has a circular reference to a JSON string.

To solve the error, create a deep copy of the dictionary when adding the circular reference or remove the circular reference before calling json.dumps().

Here is an example of how the error occurs.

main.py
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' # ๐Ÿ‘‡๏ธ this adds a circular reference a_dict['nested'] = a_dict # โ›”๏ธ ValueError: Circular reference detected json_string = json.dumps(a_dict) print(json_string)

value error circular reference detected in python

Notice that we added a circular reference to the dictionary before calling json.dumps.

A circular reference is a dictionary key that points to the dictionary itself.

In other words, it is a dictionary key that points to the same object (dictionary) in memory.

You can verify that the key points to a circular reference by using the is operator.

main.py
a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict # {'site': 'bobbyhadz.com', 'nested': {...}} print(a_dict) print(a_dict['nested'] is a_dict) # ๐Ÿ‘‰๏ธ True

verify that key points to circular reference

The code for this article is available on GitHub

You cannot convert a dictionary that has a circular reference to a JSON string as this causes the error.

# Using the dict.copy() method to solve the error

One way to solve the error is to use the dict.copy() method when adding the circular key to the dictionary.

main.py
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict.copy() json_string = json.dumps(a_dict) # ๐Ÿ‘‡๏ธ {"site": "bobbyhadz.com", "nested": {"site": "bobbyhadz.com"}} print(json_string)

use dict copy method when adding circular reference

The code for this article is available on GitHub

The dict.copy() method returns a shallow copy of the dictionary.

The dictionary is no longer stored in the same location in memory, so we no longer have circular references.

If you want to create a deep copy instead of a shallow copy, use the copy.deepcopy() method.

main.py
import json import copy a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = copy.deepcopy(a_dict) json_string = json.dumps(a_dict) print(json_string)

As indicated in the method name, it creates a deep copy of the dictionary, so none of the nested references point to keys in the original dictionary.

# Removing the circular reference before calling json.dumps()

You can also remove the circular reference before calling json.dumps() method to solve the error.

main.py
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict del a_dict['nested'] json_string = json.dumps(a_dict) print(json_string)

remove circular reference before calling json dumps

The code for this article is available on GitHub

We used the del statement to remove the nested key from the dictionary.

This is the only key that points to a circular reference, so passing the modified dictionary to json.dumps() succeeds.

# Removing circular refs with a reusable function

If you need a more robust approach for removing the circular references from an object, use the function from this stackoverflow post.

main.py
import json def remove_circular_refs(ob, _seen=None): if _seen is None: _seen = set() if id(ob) in _seen: return None _seen.add(id(ob)) res = ob if isinstance(ob, dict): res = { remove_circular_refs(key, _seen): remove_circular_refs(value, _seen) for key, value in ob.items()} elif isinstance(ob, (list, tuple, set, frozenset)): res = type(ob)(remove_circular_refs(v, _seen) for v in ob) _seen.remove(id(ob)) return res a_dict = {} a_dict['site'] = 'bobbyhadz.com' # ๐Ÿ‘‡๏ธ this adds a circular reference a_dict['nested'] = a_dict json_str = json.dumps(remove_circular_refs(a_dict)) # {"site": "bobbyhadz.com", "nested": null} print(json_str)
The code for this article is available on GitHub

The remove_circular_refs function can be used to remove circular references from dictionaries, lists, tuples, set and frozenset objects.

# Using the default argument of json.dumps() incorrectly

You might also get the error if you use the default argument of json.dumps() incorrectly.

Here is an example of how to correctly use the default argument.

main.py
import json class Employee(): def __init__(self, data): self.data = data emp1 = Employee({'site': 'bobbyhadz.com'}) def has_data_attribute(obj): if hasattr(obj, 'data'): return obj.data else: raise TypeError(f'Object must have a site attribute, but got {obj}') json_str = json.dumps(emp1, default=has_data_attribute) # ๐Ÿ‘‡๏ธ {"site": "bobbyhadz.com"} print(json_str)

using default argument of json dumps correctly

The code for this article is available on GitHub

The default argument must be set to a function that only gets called if the supplied to json.dumps() object cannot be serialized with the default behavior.

The function must return a value that can be serialized by json.dumps() or must raise a TypeError exception.

You might get the "Circular reference detected" error if instead of raising a TypeError, your function returns an object that cannot be serialized by the JSON encoder.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev