Skip to content

[Bug]: SnapShots never include app text for Flatpak and GTK4 apps on Linux #12597

Description

@Nozzit

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/desktop

Steps to reproduce

  1. GNOME 46 on Wayland, SnapShots enabled with Include app text on, org.gnome.desktop.interface toolkit-accessibility set to true.
  2. Start a Flatpak browser after enabling that setting (Zen Browser, app.zen_browser.zen) and take a SnapShot of it.
  3. Start a GTK4/libadwaita app (GNOME System Monitor, not a Flatpak) and take a SnapShot of it.

Expected behavior

Both attachments carry accessibility data. Both apps expose a full tree on the AT-SPI bus; Zen's has about 1500 elements.

Actual behavior

Both captures arrive with appName and windowTitle only. The screenshot, flash and animation are fine, and nothing is logged. There are two separate causes. I checked both by calling the bundled @crowecawcaw/xa11y native module directly from Node, with the same calls SnapShotAccessibility.ts makes.

1. Flatpak apps: the window's PID is not the PID AT-SPI reports.

readCapturedWindowAccessibility does App.byPid(active.owner.processId). For a Flatpak app the compositor reports the app's own process, but the app reaches the accessibility bus through Flatpak's xdg-dbus-proxy, so the AT-SPI registry lists the tree under the proxy's PID:

207651  /app/zen/zen --name app.zen_browser.zen    <- owns the window
207646  xdg-dbus-proxy --args=44                   <- owns the AT-SPI connection

App.byPid(207651) -> XA11Y_SELECTOR_NOT_MATCHED: No element matched selector: application[pid=207651]
App.byPid(207646) -> 1 window: {"name":"… — Zen Browser","bounds":{"x":0,"y":0,"width":1920,"height":1048}}

There is no fallback, so every Flatpak app ends up without accessibility data.

2. GTK4 apps: the window comes back without a name.

For GNOME System Monitor the PID matches and so does the size, but the element has no name and an unexpected role:

App.byPid(210600).children()[0]
  -> {"role":"group","name":null,"bounds":{"x":0,"y":0,"width":1920,"height":1048},"children":1}

same call for Zen (via the proxy PID)
  -> {"role":"window","name":"… — Zen Browser", ... "children":40}

findAccessibleWindow requires the title to match, so an unnamed window never matches. libatspi sees this window correctly: Atspi.get_desktop(0) → application gnome-system-monitor (pid 210600) → child with role frame, name System Monitor, 1920x1048. So the name is lost either inside xa11y or because children() returns a different node for GTK4's AT-SPI implementation than for other toolkits.

Impact

On a stock GNOME desktop most apps are either Flatpaks or GTK4, so Include app text rarely produces anything on Linux. It fails silently, which makes it look like the app simply has no accessible text.

Version or commit

0.0.42 (AppImage)

Environment

Zorin OS 18 (Ubuntu 24.04 base), GNOME Shell 46.0, Wayland, at-spi2-core from the distro, Flatpak Zen Browser, GNOME System Monitor with libgtk-4 and libadwaita-1.

Workaround

None found.

Possible directions: for (1), when byPid finds nothing, fall back to App.list() and match on title plus size, which is what the window matcher already relies on. For (2), when exactly one window of the right PID has matching bounds, accept it even without a name, or look at why xa11y reports GTK4 frames as unnamed groups.

Activity

  1. juliusmarminge commented on Sep 19, 2026

    @juliusmarminge
    Member

    Thanks for the unusually complete report — compositor vs AT-SPI PIDs, xa11y vs libatspi dumps, and two independent failure modes with the same calls SnapShotAccessibility.ts makes.

    Confirmed on current main. This is a real Linux SnapShot accessibility lookup/matcher bug. The screenshot path is fine; Include app text is dropped silently when the reader cannot bind a window.

    On GNOME the capture PID comes from the compositor (window.get_pid() in apps/desktop/gnome-extension/extension.js) and is passed through DesktopSnapShot.ts into readCapturedWindowAccessibility. On Linux that function only does App.byPid(active.owner.processId). There is no fallback. Misses are swallowed in the worker (run().catch(() => undefined)), so the attachment still has appName / windowTitle and no accessibility payload.

    That matches both of your cases:

    1. Flatpak — compositor PID ≠ AT-SPI PID

    The window owner is the app. The AT-SPI registry lists the tree under Flatpak’s xdg-dbus-proxy. App.byPid(appPid) raises XA11Y_SELECTOR_NOT_MATCHED; App.byPid(proxyPid) finds the named window. Because we never fall back to App.list(), every Flatpak capture loses app text.

    WindowsForegroundFocusWorker already does byPid → App.list(), but it still filters candidate.pid === target.processId, so that pattern would not fix this. xa11y’s own pytest helper already documents the same class of bug (“the process that registers with the accessibility API is not the one you launched”) and matches PID or name.

    2. GTK4 — unnamed node fails the title gate

    findAccessibleWindow in apps/desktop/src/snapShot/snapShot.ts requires a nonempty title match and bounds (±2). Tests currently reject untitled windows by size alone. For System Monitor the PID and size match, but xa11y returns { role: "group", name: null } while libatspi still sees a named frame. The title gate never matches.

    This is the same matcher family as #10896 (macOS Chrome title suffix) and a cousin of #12103 / #12524 (macOS invalid PID), but those fixes do not cover Linux AT-SPI.

    Next step

    We can fix both in t3code without waiting on xa11y:

    1. When byPid finds nothing, fall back to App.list() and reuse the existing title+size matcher. Do not require the compositor PID to equal the AT-SPI PID.
    2. When a PID-scoped lookup has exactly one window whose bounds match, accept it even if name is empty (or walk one anonymous group wrapper). Keep rejecting ambiguous size-only matches.
    3. Add regression tests for both cases.

    The GTK4 frame → unnamed group mapping is still worth an xa11y follow-up, but it should not block the matcher workaround.

    No workaround on our side today. Docs already say GNOME browsers need toolkit accessibility and a restart; that is necessary but not sufficient here.

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 19, 2026
  3. cestercian commented on Sep 19, 2026

    @cestercian
    Contributor

    Taking this — working on a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions