Skip to main content

How to Solve "ValueError: malformed node or string" with ast.literal_eval() in Python

The ValueError: malformed node or string error arises when you use the ast.literal_eval() function in Python to parse a string that doesn't represent a valid Python literal.

This guide explains the common causes of this error, provides robust solutions, and distinguishes ast.literal_eval() from json.loads().

Understanding the Error

The ast.literal_eval() function is designed to safely evaluate a string containing a Python literal (like a list, dictionary, number, string, boolean, or None). It's not a general-purpose Python code interpreter (that would be eval(), which is dangerous).

The error occurs in these main cases:

  • Incorrect Data Type: You pass a value that isn't a string to ast.literal_eval().
  • Invalid Syntax: The string doesn't represent a valid Python literal.
  • JSON Data: You are trying to use ast.literal_eval() to parse a JSON string (use json.loads() instead).

Example of the error:

import ast

a_dict = {
'first': 'tom',
'last': 'nolan',
}

# ⛔️ ValueError: malformed node or string: {'first': 'tom', 'last': 'nolan'}
try:
print(ast.literal_eval(a_dict))
except ValueError as e:
print(e)
  • The ast.literal_eval() method expects a string as an argument, not a dictionary.

Solutions

Passing Valid Python Literals

Ensure the string you pass to ast.literal_eval() represents a valid Python literal:

import ast

a_str = '''{
"first": "tom",
"last": "nolan"
}''' # Notice that this is now a string representing a dictionary.

result = ast.literal_eval(a_str) # This will work correctly
print(result)
print(type(result)) # Output: <class 'dict'>

Checking for String Type Before Evaluation

If you're uncertain about the input type, check if it's a string before calling ast.literal_eval():

import ast

a_dict = {
'first': 'tom',
'last': 'nolan',
}

if isinstance(a_dict, str):
result = ast.literal_eval(a_dict)
else:
result = a_dict

print(result) # Output: {'first': 'tom', 'last': 'nolan'}
print(type(result)) # Output: <class 'dict'>
  • If the type of the value is string, then call ast.literal_eval() on it, otherwise return the original value.

Using json.loads() for JSON Strings (Important Distinction)

ast.literal_eval() is not for parsing JSON. If you have a JSON string, use json.loads() instead:

import json

a_str = '''{
"first": "tom",
"last": "nolan",
"is_programmer": true,
"salary": null
}'''

result = json.loads(a_str) # Use json.loads for JSON

print(result) # Output: {'first': 'tom', 'last': 'nolan', 'is_programmer': True, 'salary': None}
print(type(result)) # Output: <class 'dict'>
  • The json.loads() parses a JSON string and returns a python object, a dictionary in this case.
warning

Using ast.literal_eval() with a JSON string will cause an error, because the JSON syntax is different than the Python syntax. ast.literal_eval() expects booleans to have first letter capitalized: True and False, as well as None, unlike JSON which uses lowercase true, false, and null.

Conclusion

The ValueError: malformed node or string error with ast.literal_eval() indicates that the input isn't a valid Python literal string.

Always check the input type, ensure the string represents a valid literal, and use json.loads() for JSON data.

By understanding the intended use of ast.literal_eval(), you can avoid this error and safely evaluate string representations of Python literals.