{"record":{"id":"41641188d9ec9936","repo":"Hmbown/CodeWhale","slug":"scroll-on-wayland-is-not-available-in-this-build-use-swipe","errorCode":null,"errorMessage":"scroll on Wayland is not available in this build; use swipe-style drags or run an X11/XWayland window. (Roadmap: ydotool wheel events.)","messagePattern":"scroll on Wayland is not available in this build; use swipe-style drags or run an X11/XWayland window\\. \\(Roadmap: ydotool wheel events\\.\\)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":614,"sourceCode":"        if (session === \"x11\") await xdotool([\"mousedown\", \"1\"]);\n        else await ydotool([\"click\", \"0x40\"]);\n      } catch (err) { await releaseMouse(); throw err; }\n      return { action_sent: true };\n    },\n    left_mouse_up: async () => {\n      if (!mouseHeld) throw Object.assign(new ExecError(\"no agent pointer press to release\"), { code: \"input_not_held\" });\n      await releaseMouse();\n      return { action_sent: true };\n    },\n    scroll: async ({ target, direction = \"down\", amount = 3 }) => {\n      await inputMove(target.x, target.y);\n      if (session === \"x11\") {\n        const buttons = { down: 5, up: 4, right: 7, left: 6 };\n        await xdotool([\"click\", \"--repeat\", String(Math.max(1, Math.min(30, amount))), \"--delay\", \"60\", String(buttons[direction] ?? 5)]);\n        return { action_sent: true, direction, amount };\n      }\n      // Wayland: synthesize wheel via ydotool is not wired in this build — honest refusal.\n      throw new ExecError('scroll on Wayland is not available in this build; use swipe-style drags or run an X11/XWayland window. (Roadmap: ydotool wheel events.)');\n    },\n    type: async ({ text }) => {\n      if (!text) return { action_sent: false, note: \"empty text\" };\n      await probeSession();\n      if (session === \"x11\") {\n        // xdotool `type` remaps a spare keycode for characters absent from the\n        // current keymap. Two failure modes follow: a cased letter produces a\n        // single-symbol key whose XKB level 0 is the lowercase form (Ü → ü),\n        // and consecutive remaps inside one `type` call race the X server's\n        // keymap-change propagation, so non-ASCII chars intermittently drop or\n        // arrive mangled (héllo → hllo, 日本 → 本). Route every non-ASCII char\n        // through `key U<hex>` — one synchronous remap+press+restore per char —\n        // adding Shift only when the char is cased-uppercase, and batch ASCII\n        // runs through `type` as before.\n        let runText = \"\";\n        const chunks = [];\n        for (const ch of String(text)) {\n          if (ch.codePointAt(0) > 127) {","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L596-L632","documentation":"On X11, scroll is implemented with xdotool mouse-button 4/5/6/7 clicks. On Wayland this build has no wheel-event synthesis wired (ydotool wheel support is a roadmap item), so the library fails honestly instead of faking a scroll. There is no Wayland implementation to fall back to.","triggerScenarios":"Calling scroll (any direction/amount) while the detected session is Wayland (sway, hyprland, GNOME-Wayland, KDE-Wayland); X11-only branch not taken because probeSession() reported non-x11.","commonSituations":"Automation scripts that work on an X11 desktop failing on the developer's Wayland laptop; CI containers running Wayland compositors; mixed fleets where the same action script runs on both session types.","solutions":["Run the target app under XWayland and scroll inside that window, or force the app to X11 via env GDK_BACKEND=x11 / QT_QPA_PLATFORM=xcb.","Use swipe-style pointer drags (move_to/left_click-drag sequences) to approximate scrolling.","Install and wire ydotool with a running ydotoold if your build supports wheel events, or upgrade when the roadmap lands.","Switch the session to X11 (login X11 session) if scrolling is essential."],"exampleFix":"// before\nawait backend.scroll({ direction: 'down', amount: 5 }); // throws on Wayland\n// after\nif (sessionType !== 'x11') {\n  await backend.move_to({ x: cx, y: cy });\n  await dragSwipe({ dy: 120 }); // swipe-style drag instead\n} else {\n  await backend.scroll({ direction: 'down', amount: 5 });\n}","handlingStrategy":"fallback","validationCode":"const sess = await detectSession(); // 'x11' | 'wayland'\nif (sess !== 'x11' && action === 'scroll') useSwipeDragInstead();","typeGuard":null,"tryCatchPattern":"try {\n  await backend.scroll({ direction, amount });\n} catch (e) {\n  if (String(e.message).includes('scroll on Wayland')) {\n    await swipeDragFallback({ direction, amount });\n    return;\n  }\n  throw e;\n}","preventionTips":["Detect session type (XDG_SESSION_TYPE) up front and branch input strategies","Prefer XWayland for apps needing wheel events, or force GDK_BACKEND=x11","Keep a drag-based scroll fallback implemented for Wayland targets"],"tags":["wayland","x11","scroll","unsupported-feature","linux"],"backgroundTag":"unsupported-operation","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T21:17:16.096Z"}