{"record":{"id":"7761e90e0f357986","repo":"python/cpython","slug":"malformed-node-or-string-node-r","errorCode":null,"errorMessage":"malformed node or string: {node!r}","messagePattern":"malformed node or string: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"Lib/ast.py","lineNumber":114,"sourceCode":"            return + operand\n        else:\n            return - operand\n    if (\n        isinstance(node, BinOp)\n        and isinstance(node.op, (Add, Sub))\n        and isinstance(node.left, (Constant, UnaryOp))\n        and isinstance(node.right, Constant)\n        and type(left := _convert_literal(node.left)) in (int, float)\n        and type(right := _convert_literal(node.right)) is complex\n    ):\n        if isinstance(node.op, Add):\n            return left + right\n        else:\n            return left - right\n    msg = \"malformed node or string\"\n    if lno := getattr(node, 'lineno', None):\n        msg += f' on line {lno}'\n    raise ValueError(msg + f': {node!r}')\n\n\ndef dump(\n    node, annotate_fields=True, include_attributes=False,\n    *,\n    color=False, indent=None, show_empty=False,\n):\n    \"\"\"\n    Return a formatted dump of the tree in node.  This is mainly useful for\n    debugging purposes.\n\n    If annotate_fields is true (by default), the returned string will show the\n    names and the values for fields. If annotate_fields is false, the result\n    string will be more compact by omitting unambiguous field names.\n\n    Attributes such as line numbers and column offsets are not dumped by default.\n    If this is wanted, include_attributes can be set to true.\n","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/ast.py#L96-L132","documentation":"literal_eval walks the AST produced from the string/node and accepts only literal constructs (constants, and +,-,~ on numbers, and complex real+imag additions). Any other node type — names, calls, comprehensions, comparisons, non-numeric BinOps — falls through to the final raise: ValueError('malformed node or string[: on line N]: <node repr>'). It is a hard rejection of anything that is not a Python literal, including seemingly innocent expressions.","triggerScenarios":"ast.literal_eval('1 if x else 2') (ternary/name); literal_eval(\"{'a': {1, 2}}\") — sets are not supported; literal_eval('f\"{x}\"'); literal_eval('1 < 2') (Compare node); passing a random AST node object such as a Module where an expression node is expected. Note +1j and 'a'+'b' style numeric/string constant folding beyond the numeric cases is also rejected.","commonSituations":"Parsing JSON-ish config files that contain tuples/single quotes (literal_eval used instead of json.loads); safely evaluating 'trusted-shaped' data from environment variables or saved state that later grows non-literal syntax; deserializing data written by repr() of objects that are not pure literals.","solutions":["Ensure the input contains only literals: numbers, strings, bytes, booleans, None, tuples, lists, dicts with literal keys and values.","Replace non-literal syntax (sets -> lists, ternaries -> resolved values) at the producer side.","If the data is actually JSON, use json.loads; if you truly need expressions, that is what eval does — do it only on fully trusted input."],"exampleFix":"# before\nvalue = ast.literal_eval(\"{'a': {1, 2}}\")  # set literal -> malformed node or string\n\n# after\nvalue = ast.literal_eval(\"{'a': [1, 2]}\")  # list literal is accepted","handlingStrategy":"try-catch","validationCode":"import re\n\nLITERAL_OK = re.compile(\n    r'^[\\s\\d\\.eEjJ+-()\\[\\]{},:\\'\"_abfnrtxuU]*$')  # cheap smoke test only\n\ndef probably_literal(s):\n    return LITERAL_OK.match(s) is not None","typeGuard":"def is_literal_string(s):\n    try:\n        ast.literal_eval(s)\n    except (ValueError, SyntaxError):\n        return False\n    return True","tryCatchPattern":"try:\n    value = ast.literal_eval(raw)\nexcept ValueError as e:\n    if 'malformed node or string' in str(e):\n        raise ConfigError(f'non-literal data in config: {raw[:80]!r}') from None","preventionTips":["Write configs with repr() of pure literals only, or switch to json.dumps/json.loads.","Reject sets, ternaries, names and calls at the producer side — literal_eval will not accept them.","Treat literal_eval failures as untrusted input, never fall back to eval()."],"tags":["ast","literal-eval","deserialization","valueerror"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}