{"record":{"id":"616d66f61825c7a2","repo":"ScrapeGraphAI/Scrapegraph-ai","slug":"missing-or-unbalanced-parentheses-in-expression-616d66","errorCode":null,"errorMessage":"Missing or unbalanced parentheses in expression.","messagePattern":"Missing or unbalanced parentheses in expression\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scrapegraphai/utils/parse_state_keys.py","lineNumber":73,"sourceCode":"        or \"||\" in expression\n        or \"&|\" in expression\n        or \"|&\" in expression\n    ):\n        raise ValueError(\"Invalid operator usage.\")\n\n    open_parentheses = close_parentheses = 0\n    for i, char in enumerate(expression):\n        if char == \"(\":\n            open_parentheses += 1\n        elif char == \")\":\n            close_parentheses += 1\n        if char in \"&|\" and i + 1 < len(expression) and expression[i + 1] in \"&|\":\n            raise ValueError(\n                \"Invalid operator placement: operators cannot be adjacent.\"\n            )\n\n    if open_parentheses != close_parentheses:\n        raise ValueError(\"Missing or unbalanced parentheses in expression.\")\n\n    def evaluate_simple_expression(exp):\n        for or_segment in exp.split(\"|\"):\n            and_segment = or_segment.split(\"&\")\n            if all(elem.strip() in state for elem in and_segment):\n                return [elem.strip() for elem in and_segment if elem.strip() in state]\n        return []\n\n    def evaluate_expression(expression):\n        while \"(\" in expression:\n            start = expression.rfind(\"(\")\n            end = expression.find(\")\", start)\n            sub_exp = expression[start + 1 : end]\n            sub_result = evaluate_simple_expression(sub_exp)\n            expression = (\n                expression[:start] + \"|\".join(sub_result) + expression[end + 1 :]\n            )\n        return evaluate_simple_expression(expression)","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/ScrapeGraphAI/Scrapegraph-ai/blob/532dfffbf6ee823a6c9cf8cfedc24a93bf026780/scrapegraphai/utils/parse_state_keys.py#L55-L91","documentation":"ValueError from parse_expression: the number of '(' does not equal the number of ')' in the expression. Unbalanced parentheses make the grouping undecidable.","triggerScenarios":"Expressions like '(a & b' or 'a | b))' passed to parse_expression.","commonSituations":"Dynamically composed conditions where a closing paren is dropped, or hand-written config strings with a typo.","solutions":["Balance the parentheses: '(a | b) & c'","Lint edge-condition expressions in tests before deploying graph configs"],"exampleFix":"# before\nexpr = '(a & (b | c)'\n# after\nexpr = '(a & (b | c))'","handlingStrategy":"validation","validationCode":"assert expression.count(\"(\") == expression.count(\")\"), \"unbalanced parentheses\"","typeGuard":"def has_balanced_parens(expr) -> bool:\n    return expr.count(\"(\") == expr.count(\")\")","tryCatchPattern":"try:\n    keys = parse_expression(expr, state)\nexcept ValueError as e:\n    if expr.count(\"(\") != expr.count(\")\"):\n        expr += \")\" * (expr.count(\"(\") - expr.count(\")\"))\n        keys = parse_expression(expr, state)\n    else:\n        raise","preventionTips":["Balance-check expressions in config validation","Use an editor paren-matching when editing conditions","Test dynamic expression builders for paren symmetry"],"tags":["expression","state-keys","validation"],"backgroundTag":"unbalanced-parentheses","analyzedSha":"532dfffbf6ee823a6c9cf8cfedc24a93bf026780","analyzedAt":"2026-08-28T15:19:38.821Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}