{"record":{"id":"1d08485adb82338c","repo":"stablyai/orca","slug":"direction-is-required","errorCode":null,"errorMessage":"direction is required","messagePattern":"direction is required","errorType":"validation","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"native/computer-use-linux/runtime.py","lineNumber":884,"sourceCode":"    command = [xdotool, \"mousemove\", \"--sync\", str(round(x)), str(round(y))]\n    for modifier in modifier_keys:\n        command.extend([\"keydown\", modifier])\n    command.extend([\"click\", \"--repeat\", str(count), \"--delay\", \"35\", button_number])\n    for modifier in reversed(modifier_keys):\n        command.extend([\"keyup\", modifier])\n    try:\n        subprocess.run(command, check=True, timeout=5)\n    finally:\n        subprocess.run(\n            [xdotool, *[item for modifier in reversed(modifier_keys) for item in (\"keyup\", modifier)]],\n            check=False,\n            timeout=2,\n        )\n\n\ndef scroll_at(x, y, direction, pages):\n    if direction is None or str(direction).strip() == \"\":\n        raise RuntimeError(\"direction is required\")\n    direction = str(direction).lower()\n    wheel_events = {\n        \"up\": (\"b4p\", \"b4r\"),\n        \"down\": (\"b5p\", \"b5r\"),\n        \"left\": (\"b6p\", \"b6r\"),\n        \"right\": (\"b7p\", \"b7r\"),\n    }\n    if direction not in wheel_events:\n        raise RuntimeError(f\"unsupported scroll direction: {direction}\")\n    down, up = wheel_events[direction]\n    page_count = max(1, math.ceil(require_positive_number(1 if pages is None else pages, \"pages\")))\n    for _ in range(page_count):\n        Atspi.generate_mouse_event(round(x), round(y), \"abs\")\n        Atspi.generate_mouse_event(round(x), round(y), down)\n        time.sleep(0.03)\n        Atspi.generate_mouse_event(round(x), round(y), up)\n\n","sourceCodeStart":866,"sourceCodeEnd":902,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/native/computer-use-linux/runtime.py#L866-L902","documentation":"Raised by scroll_at (runtime.py:884) when direction is None or strips to an empty string. Direction is a required field for the scroll tool because the wheel-event mapping (up/down/left/right) depends on it. This fires before the direction is lowercased and looked up.","triggerScenarios":"Calling the 'scroll' tool with operation['direction'] missing, null, or whitespace-only ('   '). An empty string after .strip() is treated as absent.","commonSituations":"An agent omits direction assuming a default; a caller forwards a templated field that resolved to empty; whitespace from a malformed JSON value.","solutions":["Always set 'direction' to one of 'up', 'down', 'left', 'right' for scroll operations.","If your controller has a default scroll direction, populate the field explicitly before dispatch.","Reject empty direction in your own input layer rather than relying on the runtime."],"exampleFix":"// before\n{\"tool\":\"scroll\",\"pages\":2}\n// after\n{\"tool\":\"scroll\",\"direction\":\"down\",\"pages\":2}","handlingStrategy":"validation","validationCode":"DIRECTIONS = {\"up\", \"down\", \"left\", \"right\"}\ndirection = (raw_direction or \"\").strip().lower()\nif direction not in DIRECTIONS:\n    direction = \"down\"  # sane default\nop = {\"tool\": \"scroll\", \"direction\": direction}","typeGuard":"def is_scroll_direction(value) -> bool:\n    return (value or \"\").strip().lower() in {\"up\", \"down\", \"left\", \"right\"}","tryCatchPattern":"try:\n    run_operation(op)\nexcept RuntimeError as e:\n    if \"direction is required\" in str(e):\n        op[\"direction\"] = \"down\"\n    else:\n        raise","preventionTips":["Always set 'direction' explicitly for scroll operations.","Default to a direction in your controller rather than sending empty.","Validate direction before dispatch."],"tags":["validation","input-validation","computer-use","scroll","python"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}