{"record":{"id":"3b73acb31af8b870","repo":"Hmbown/CodeWhale","slug":"at-spi-action-failed-r-stderr-r-stdout-slice-0-250","errorCode":null,"errorMessage":"AT-SPI action failed: ${(r.stderr || r.stdout).slice(0, 250)}","messagePattern":"AT-SPI action failed: (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":303,"sourceCode":"        break\n    if len(p) > 12: continue\n    try:\n        for i in range(n.childCount):\n            c = n.getChildAtIndex(i)\n            if c: stack.append((c, p + [i]))\n    except Exception: pass\nif found is None:\n    print(json.dumps({\"ok\": False, \"code\": \"element_stale\"}))\n    sys.exit(0)\ntry:\n${pythonBody}\nexcept Exception as e:\n    print(json.dumps({\"ok\": False, \"code\": str(e)}))`;\n    const argv = [\"-c\", script, name, JSON.stringify(target.path ?? [])];\n    if (extraArg != null) argv.push(String(extraArg));\n    const r = await run(\"python3\", argv, { timeoutMs: 30_000 });\n    const out = tryJson((r.stdout.trim().split(\"\\n\").pop() ?? \"\"), null);\n    if (!out) throw new ExecError(`AT-SPI action failed: ${(r.stderr || r.stdout).slice(0, 250)}`, r);\n    return out;\n  }\n\n  // ---------- input helpers ----------\n  function clickButton(button, clicks) {\n    if (session === \"x11\") {\n      const args = [\"click\"];\n      if (clicks > 1) args.push(\"--repeat\", String(clicks), \"--delay\", \"80\");\n      args.push(String(button));\n      return xdotool(args);\n    }\n    // ydotool click mask: down|up|count nibble (0xC0 = left click once, +1 per extra click;\n    // 0x04 bit selects right button, 0x02 middle).\n    const count = Math.max(1, Math.min(3, clicks));\n    const code = button === 3 ? 0xc0 + count + 0x04 : button === 2 ? 0xc0 + count + 0x02 : 0xc0 + count - 1;\n    return ydotool([\"click\", \"0x\" + code.toString(16)]);\n  }\n","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L285-L321","documentation":"Thrown by atspiResolve when the embedded python3 AT-SPI script either fails to produce parseable JSON on its last stdout line or crashes. The message carries up to 250 chars of stderr (falling back to stdout) prefixed with 'AT-SPI action failed'. The wrapper runs `python3 -c <script> <name> <path-json>` with a 30s timeout, then tryJson()es the final line.","triggerScenarios":"Any AT-SPI-backed action (list_apps, tree resolution, invoke action) where: python3 is missing/broken, pyatspi/gi is not installed, the target application or path no longer exists (script prints its exception JSON or tracebacks to stderr), or stdout's last line is not the expected JSON object.","commonSituations":"Minimal Linux installs without python3-pyatspi / gir1.2-atspi-2.0; running outside a desktop session so no AT-SPI bus is available; app closed between listing and acting so the resolved path is stale; accessibility not enabled in the session.","solutions":["Read the 250-char payload: a Python traceback means missing modules or no AT-SPI bus; {\"ok\":false,\"code\":...} means the lookup/action itself failed.","Install accessibility bindings: python3-pyatspi and at-spi2-related GIR packages for your distro.","Confirm `python3 -c 'import pyatspi'` works in the same environment/user session.","Ensure the desktop session exposes the accessibility bus (AT_SPI_BUS / accessibility enabled in the session settings).","Re-list apps/windows and retry — the target's path may be stale after UI changes."],"exampleFix":"// before (Debian/Ubuntu, missing bindings)\n# python3 exists but pyatspi doesn't -> AT-SPI action failed\n// after\nsudo apt install python3-pyatspi gir1.2-atspi-2.0","handlingStrategy":"try-catch","validationCode":"import { execFileSync } from 'child_process';\nfunction atspiReady() {\n  try {\n    execFileSync('python3', ['-c', 'import pyatspi'], { stdio: 'ignore', timeout: 10_000 });\n    return true;\n  } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const out = await atspiResolve(name, target, extraArg);\n} catch (e) {\n  if (e instanceof ExecError && e.message.startsWith('AT-SPI action failed')) {\n    if (/ModuleNotFoundError|ImportError/.test(e.message)) installAtspiBindings();\n    else if (/Traceback|No display|bus/i.test(e.message)) ensureDesktopSession();\n    else await refreshTargetsAndRetry(); // stale path — re-list and retry once\n  } else throw e;\n}","preventionTips":["Install python3-pyatspi and the at-spi GIR bindings on the target machine.","Enable accessibility in the desktop session so the AT-SPI bus is exposed.","Re-resolve app/window paths right before acting; UIs change asynchronously.","Run the plugin inside the logged-in desktop session's user/bus environment.","Smoke-test `python3 -c 'import pyatspi'` in CI images."],"tags":["subprocess","atspi","accessibility","python","linux"],"backgroundTag":"missing-dependency","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}