{"record":{"id":"823dfe6134552b2f","repo":"HKUDS/Vibe-Trading","slug":"unsupported-unary-operator-type-node-op-name","errorCode":null,"errorMessage":"unsupported unary operator: {type(node.op).__name__}","messagePattern":"unsupported unary operator: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/financial_rigor_tool.py","lineNumber":109,"sourceCode":"        The exact Decimal value of the node.\n\n    Raises:\n        ValueError: If the node is not a supported numeric/arithmetic form.\n    \"\"\"\n    if isinstance(node, ast.Constant):\n        # bool is a subclass of int — reject it explicitly.\n        if isinstance(node.value, bool) or not isinstance(node.value, (int, float)):\n            raise ValueError(\"only numeric constants are allowed\")\n        return _exact(node.value)\n    if isinstance(node, ast.BinOp):\n        op_fn = _AST_BINOPS.get(type(node.op))\n        if op_fn is None:\n            raise ValueError(f\"unsupported operator: {type(node.op).__name__}\")\n        return op_fn(_eval_arith_node(node.left), _eval_arith_node(node.right))\n    if isinstance(node, ast.UnaryOp):\n        op_fn = _AST_UNARYOPS.get(type(node.op))\n        if op_fn is None:\n            raise ValueError(f\"unsupported unary operator: {type(node.op).__name__}\")\n        return op_fn(_eval_arith_node(node.operand))\n    raise ValueError(f\"disallowed element in expression: {type(node).__name__}\")\n\n\ndef _safe_arith(expr: str) -> Decimal:\n    \"\"\"Evaluate a numeric arithmetic expression in the exact-Decimal domain.\n\n    The expression is parsed and evaluated recursively with Decimal arithmetic,\n    so ``0.1 + 0.2`` is exactly ``0.3`` — no IEEE-754 drift, and no ``eval``.\n    Only numbers and the operators ``+ - * /`` (with optional unary sign) are\n    permitted; any other AST node raises ``ValueError``.\n\n    Args:\n        expr: Arithmetic expression string, e.g. ``\"510 * 9.11e9\"``.\n\n    Returns:\n        The exact Decimal result.\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/financial_rigor_tool.py#L91-L127","documentation":"Raised when a UnaryOp in the arithmetic AST uses an operator absent from _AST_UNARYOPS (typically only UAdd and USub). Operators like `not` (ast.Not) or `~` (ast.Invert) are rejected to keep the evaluator in the pure numeric Decimal domain. The message names the operator class for diagnostics.","triggerScenarios":"Calling exact_calc/_safe_arith with expressions like 'not 1', '~5', or any unary operator other than +/-.","commonSituations":"Users pasting boolean logic or bitwise negation into a strictly numeric financial calculator; LLM-generated expressions mixing boolean and arithmetic syntax.","solutions":["Remove boolean/bitwise unary operators from the expression","Pre-validate the expression string with a regex/AST check before submitting","Extend _AST_UNARYOPS only if a Decimal-safe semantics exists for the operator"],"exampleFix":"// before\n_safe_arith(\"~0\")\n// after\n_safe_arith(\"-0\")","handlingStrategy":"validation","validationCode":"import ast\nexpr = \"~5\"\ntree = ast.parse(expr, mode=\"eval\")\nassert all(type(n.op) in (ast.UAdd, ast.USub) for n in ast.walk(tree) if isinstance(n, ast.UnaryOp)), \"disallowed unary op\"","typeGuard":"def has_only_numeric_unary(expr: str) -> bool:\n    import ast\n    try:\n        tree = ast.parse(expr, mode=\"eval\")\n    except SyntaxError:\n        return False\n    return all(type(n.op) in (ast.UAdd, ast.USub) for n in ast.walk(tree) if isinstance(n, ast.UnaryOp))","tryCatchPattern":"try:\n    value = _safe_arith(expr)\nexcept ValueError as e:\n    if \"unary operator\" in str(e):\n        raise UserInputError(f\"rewrite {expr!r} without boolean/bitwise unary ops\")\n    raise","preventionTips":["Strip 'not ', '~', '!' from generated expressions","Pre-parse expressions to enumerate operators before evaluation"],"tags":["python","ast","sandbox","expression-eval","validation"],"backgroundTag":"expression-not-allowed","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}