{"record":{"id":"199dd1610f0c43d0","repo":"Hmbown/CodeWhale","slug":"at-spi-resolve-failed-r-stderr-r-stdout-slice-0-250","errorCode":null,"errorMessage":"AT-SPI resolve failed: ${(r.stderr || r.stdout).slice(0, 250)}","messagePattern":"AT-SPI resolve failed: (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/linux.mjs","lineNumber":557,"sourceCode":"    except Exception:\n        found = None\n    if found is None:\n        ok = False\n        break\n    node = found\nif not ok:\n    print(json.dumps({\"found\": True, \"element\": None, \"reason\": \"element_stale\"}))\n    sys.exit(0)\next = None\ntry: ext = node.queryComponent().getExtents(pyatspi.DESKTOP_COORDS)\nexcept Exception: pass\nprint(json.dumps({\"found\": True, \"reason\": None, \"element\": {\n    \"role\": node.getRoleName() or None, \"label\": node.name or None,\n    \"position\": {\"x\": ext.x, \"y\": ext.y} if ext else None,\n    \"size\": {\"w\": ext.width, \"h\": ext.height} if ext else None}}))`;\n      const r = await run(\"python3\", [\"-c\", script, name, JSON.stringify(pathArr ?? [])], { timeoutMs: 30_000 });\n      const out = tryJson(r.stdout.trim().split(\"\\n\").pop() ?? \"\", null);\n      if (!out) throw new ExecError(`AT-SPI resolve failed: ${(r.stderr || r.stdout).slice(0, 250)}`, r);\n      return out;\n    },\n    zoom: async ({ source, region, path: outPath }) => {\n      need(\"ffmpeg\", \"zoom/crop\");\n      const src = source ?? lastRaster?.file;\n      if (!src) throw new ExecError(\"no screenshot taken yet on this computer — call screenshot first\");\n      const out = outputPath(outPath ?? path.join(recordingsDir(), `zoom-${crypto.randomBytes(4).toString(\"hex\")}.png`));\n      await runOk(\"ffmpeg\", [\"-y\", \"-loglevel\", \"error\", \"-i\", src, \"-vf\", `crop=${Math.round(region[2])}:${Math.round(region[3])}:${Math.round(region[0])}:${Math.round(region[1])}`, out], { timeoutMs: 20_000 });\n      return { file: out, bytes: fs.statSync(out).size, region, source: src };\n    },\n    left_click: ({ target, strategy }) => { assertNum(target.x, \"x\"); assertNum(target.y, \"y\"); assertEventStrategy(strategy); return inputChain(target.x, target.y, () => clickButton(1, 1)); },\n    double_click: ({ target }) => inputChain(target.x, target.y, () => clickButton(1, 2)),\n    triple_click: ({ target }) => inputChain(target.x, target.y, () => clickButton(1, 3)),\n    right_click: ({ target }) => inputChain(target.x, target.y, () => clickButton(3, 1)),\n    middle_click: ({ target }) => inputChain(target.x, target.y, () => clickButton(2, 1)),\n    mouse_move: ({ target }) => inputMove(target.x, target.y),\n    left_click_drag: async ({ from_target: from, to }) => {\n      requireInputOwner();","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/linux.mjs#L539-L575","documentation":"The internal atspiResolve helper runs an embedded python3 script to locate an element in the AT-SPI tree; this error is thrown when its output cannot be parsed as JSON. The first 250 bytes of stderr (or stdout) are included to show why the script failed.","triggerScenarios":"The python script raised an exception (stderr traceback) — bad element path, pyatspi errors, node disappeared mid-walk; python3 or pyatspi missing so stdout is empty; the 30s timeout elapsing and killing the interpreter; the target element being stale (window closed between listing and resolve).","commonSituations":"Automating a window that was closed or navigated away between actions; passing an element path from a previous get_app_state snapshot that no longer resolves; machines missing python3-pyatspi so every resolve fails.","solutions":["Read the truncated stderr in the message to identify the python failure, then fix that root cause.","Install python3-pyatspi and verify import pyatspi works.","Re-fetch a fresh element path via get_app_state before resolving — stale paths from old snapshots fail.","Ensure the target window/app is still open and the a11y bus is running; retry the action."],"exampleFix":"// before\nawait backend.click({ target: { path: oldPath } }); // stale path -> AT-SPI resolve failed\n// after\nconst state = await backend.get_app_state({ app_ref: { name: 'Firefox' } });\nconst fresh = findElement(state, 'Submit');\nawait backend.click({ target: { path: fresh.path } });","handlingStrategy":"retry","validationCode":"const fresh = await backend.get_app_state({ app_ref }); // refresh paths before resolving/clicking","typeGuard":null,"tryCatchPattern":"try {\n  return await resolveElement(target);\n} catch (e) {\n  if (String(e.message).startsWith('AT-SPI resolve failed')) {\n    await refreshAppState();\n    return resolveElement(freshTargetFor(target));\n  }\n  throw e;\n}","preventionTips":["Always resolve element paths from a freshly taken app state snapshot","Check stderr excerpt in the message for the real python failure","Install python3-pyatspi before any AT-SPI-based operation","Avoid operating on windows that may close between listing and resolution"],"tags":["linux","accessibility","atspi","python","parse-failure"],"backgroundTag":"json-parse-error","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"}