{"record":{"id":"6b3251bc37b67fc3","repo":"HKUDS/Vibe-Trading","slug":"unsupported-operator-type-node-op-name","errorCode":null,"errorMessage":"unsupported operator: {type(node.op).__name__}","messagePattern":"unsupported operator: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/tools/financial_rigor_tool.py","lineNumber":104,"sourceCode":"\n    Args:\n        node: An AST node from a parsed expression.\n\n    Returns:\n        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:","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/tools/financial_rigor_tool.py#L86-L122","documentation":"Raised by the sandboxed arithmetic evaluator in financial_rigor_tool when an ast.BinOp uses an operator not present in _AST_BINOPS (only the basic numeric operators are whitelisted). This is a deliberate security restriction: arbitrary Python operators (e.g. bitwise |, &, <<) are rejected so the exact-Decimal calculator cannot be abused. The error names the offending operator class so you can see exactly what was disallowed.","triggerScenarios":"Calling exact_calc (or _safe_arith) with an expression containing a binary operator outside the whitelist, e.g. '1 | 2', '5 << 1', or '3 ^ 2'. Also triggered when a caller attempts Python syntax features that parse as BinOp with exotic ops.","commonSituations":"Agents or users passing bitwise/boolean expressions into a financial rigor tool that only supports +, -, *, /, //, %, **; pasting raw Python snippets into the calculator.","solutions":["Rewrite the expression using only whitelisted arithmetic operators (+ - * / // % **)","If a legitimate operator is missing, extend the _AST_BINOPS mapping in agent/src/tools/financial_rigor_tool.py with a Decimal-safe implementation","Validate/normalize expressions upstream before calling exact_calc"],"exampleFix":"// before\nexact_calc(\"shares = 1024 | 512\")\n// after\nexact_calc(\"shares = 1024 + 512\")","handlingStrategy":"validation","validationCode":"import ast\nALLOWED_BINOPS = {ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow}\nexpr = \"1 | 2\"\ntree = ast.parse(expr, mode=\"eval\")\nbad = [n.op for n in ast.walk(tree) if isinstance(n, ast.BinOp) and type(n.op) not in ALLOWED_BINOPS]\nassert not bad, f\"disallowed operators: {bad}\"","typeGuard":"def uses_only_whitelisted_ops(expr: str) -> bool:\n    import ast\n    try:\n        tree = ast.parse(expr, mode=\"eval\")\n    except SyntaxError:\n        return False\n    ok = {ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.Pow}\n    return all(type(n.op) in ok for n in ast.walk(tree) if isinstance(n, ast.BinOp))","tryCatchPattern":"try:\n    result = exact_calc(expr)\nexcept ValueError as e:\n    if \"unsupported operator\" in str(e):\n        expr = sanitize(expr)  # strip/replace disallowed ops\n        result = exact_calc(expr)\n    else:\n        raise","preventionTips":["Whitelist characters/operators in the expression before submission","Document the supported operator set in tool descriptions given to LLMs","Reject anything beyond + - * / // % ** unary +/- and literals"],"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"}