Kepler automation-toolkit `/screenshot` returns empty frames after video playback (persists until reboot)

# Bug report — Vega/Kepler automation-toolkit `/screenshot` (submission form)

## :backhand_index_pointing_right: Bug Description

## 1. Summary

The Kepler automation toolkit’s W3C `GET /session/:id/screenshot` endpoint (used via

`@amazon-devices/appium-kepler-driver`) returns an **empty payload**

(`HTTP 200, {“value”: null}`, 0 bytes) as soon as **DRM-protected VOD content**

starts playing in the app. It does **not** recover for the rest of the device’s

uptime — surviving app force-relaunch, Appium session recreation, and re-creating

the toolkit enable file. Only a **device reboot** restores screenshots. Pre-roll

ads still capture fine (a 4 MB screenshot is captured mid-ad in our logs); the

endpoint only breaks when the protected feature video begins. Impact: automated UI

screenshots are lost from the first protected playback onward, removing visual

evidence from test runs (functional/element-tree verification is unaffected).

**App Name:** My app

**App Link on Amazon Appstore:**

**Bug Severity** (select one):

- [ ] Impacts operation of app

- [x] **Other** — Defect in the SDK automation toolkit / Kepler driver

  (\`/screenshot\`). Affects **\*\*automated UI testing\*\*** (screenshot capture), not

  the app's runtime playback. \*(If your triage prefers it, this can also be

  filed as "Blocks current development" — it blocks automated screenshot

  evidence in our CI test suite.)\*

- [ ] Blocks current development

- [ ] Improvement suggestion

- [ ] Issue with documentation

- [ ] Other

### 2. Steps to Reproduce

1. Connect to the device via the Kepler driver (`appium-kepler-driver`,

`automationName: automation-toolkit/JSON-RPC`) and create an Appium session over

`vda://:5555`.

2. Call `GET /session/:id/screenshot` — confirm it returns a valid PNG (~2 MB at

1920×1080). :white_check_mark: works.

3. In the app, open and play **DRM-protected VOD content** through to the main

feature (i.e. past any pre-roll ads — ads alone do **not** trigger the bug).

4. Call `GET /session/:id/screenshot` again while/after the protected content plays.

5. Observe the endpoint now returns an empty payload, and keeps doing so for every

subsequent call — even after the video stops, the app is force-killed

(`vlcm terminate-app --pkg-id --force`) and relaunched (`vmsgr send

orpheus://`), and the Appium session is recreated.

**Specific component / method triggering it:** the WebDriver/Appium

`GET /session/:id/screenshot` endpoint served by the on-device automation toolkit

(Kepler driver `KeplerDriver`). Not a React Native app-code call.

### 3. Observed Behavior

- Before any protected playback, `/screenshot` reliably returns full PNGs

(~2.0–2.5 MB; one 4 MB capture succeeded **while a pre-roll ad was on screen**).

- The instant the **ad ends and the DRM-protected feature video renders**, every

`/screenshot` call returns `HTTP 200` with `{“value”: null}` (0 bytes).

- The empty state **persists for the rest of device uptime**:

  • it remains empty after the video ends and normal UI is back on screen,

  • `vlcm terminate-app --force` + `vmsgr` relaunch of the app does not clear it,

  • recreating the Appium session does not clear it,

  • re-creating `/tmp/automation-toolkit.enable` does not clear it,

  • the on-device Wayland `screenshooter` fallback also cannot capture (it blocks

    until timeout — 0 successful frames).

- A run started after a prior playback run is **blank from step 1** (see log B).

- **Only a reboot** (`vega exec vda reboot`) restores `/screenshot`.

### 4. Expected Behavior

`GET /session/:id/screenshot` should return a PNG of the current screen. For

DRM-protected/secure surfaces where the framebuffer legitimately cannot be read, it

should return a **black/placeholder frame** (or an explicit, distinct error) rather

than an empty `{“value”: null}`, and it should **recover automatically once the

protected content is no longer on screen** (after playback ends / app exit) without

requiring a device reboot.

### 4.a Possible Root Cause & Temporary Workaround

**Likely root cause (hypothesis):** during the protected feature, the compositor

(Weston) promotes the video to a **secure/protected hardware overlay plane**

(HDCP / Widevine-backed) that the toolkit’s frame grabber and the Wayland

`screenshooter` cannot read back, and the protected-plane / capture path is **not

torn down** when playback ends or the app exits — so it stays “unreadable” until

reboot. The ad-vs-feature split (ads capture fine, protected feature does not)

points at content protection rather than “any video”.

**Why we cannot work around it from automation:** the toolkit `mobile: shell` runs

as an unprivileged app sandbox (`uid=5000 app_user`); `systemctl is-system-running`

→ `offline` and `cat /proc/1/comm` → `Permission denied` (process enumeration

blocked), so it cannot restart the compositor or media/DRM services. See attached

`C_capability_probe_20260625.txt`.

**Temporary workaround in use:** detect the stuck state (a `/screenshot` that

returns empty after a previously-successful capture) and reboot the device between test sections, then

continue. Screenshots are sacrificed until the reboot; functional checks continue

via the element tree.

### 5. Logs or crash report

Not a crash — no crash log. Attached app/device logs (ANSI-stripped run logs +

capability probe):

- **`A_Run_20260625_172304_worked_then_broke.log`** — 15 successful captures

(incl. a 4 MB capture at `17:25:37` *during* the ad), then empty from `17:26:02`

when the protected feature starts; never recovers.

- **`B_Run_20260625_173026_blank_from_start.log`** — empty from step 1 despite a

fresh enable-file touch + app force-relaunch (`17:30:37–39`).

- **`C_capability_probe_20260625.txt`** — unprivileged-sandbox evidence (systemd

offline, `/proc` enumeration blocked) showing no soft reset is possible.

- `README.md` — maps the above to the exact lines to read.

*(All four are in the `bug_logs/` folder accompanying this report.)*

## 6. Environment

**SDK Version:** `0.22`

**App State:** Foreground (app is in the foreground, actively playing VOD).

Additional environment (for context):

- Device: Amazon Fire TV (Kepler / Vega), physical device (arm), VDA serial

`G071R20720350DT6`, connected `vda://192.168.x.xx:5555`.

- Appium driver: `@amazon-devices/appium-kepler-driver` **v3.30.0**

(`automationName: automation-toolkit/JSON-RPC`), Appium 2.x on the host.

- Host: Windows 11; test harness uses direct W3C REST calls to the driver.

- Screenshot when working: 1920×1080 PNG, ~2.0–2.5 MB.

- Session capabilities:

```json

{

"platformName": "Kepler",

"appium:automationName": "automation-toolkit/JSON-RPC",

"kepler:device": "vda://192.168.x.xx:5555",

"appium:appURL": "my app",

"appium:newCommandTimeout": 120

}

```

### 7. Example Code Snippet / Screenshots / Screengrabs

Not React Native app code — the trigger is the WebDriver screenshot call. Minimal

repro against a live session (any HTTP client):

```

# 1. works before protected playback → returns a base64 PNG in “value”

GET http://127.0.0.1:4723/session//screenshot

# 2. start DRM-protected VOD (past the ads), then call again:

GET http://127.0.0.1:4723/session//screenshot

# → HTTP 200 {“value”: null} (0 bytes) — and stays empty until reboot

```

Screenshots: cannot attach a “stuck” screenshot — the endpoint returns nothing.

The byte-count timeline in `A_…log` is the evidence (full PNG → null).

## :backhand_index_pointing_right: Playback Issues

This is screenshot-capture-during-playback, so the playback context matters:

- **Content:** DRM-protected VOD in RTL+ (RTLDE), title *"Bandit – Catch Him If You

Can"* (deeplink_id `1670749`), opened in-app by search/deeplink.

- **Pre-conditions:** RTL+ catalog is **geo-restricted to Germany**; guest (free)

user; pre-roll ads play before the feature.

- **Architecture:** arm (physical Fire TV device), not the x86 simulator.

Additional details:

**Q: media/content URL**

**Q: special headers required** — N/A (no manual headers; auth/DRM handled in-app).

Note: the bug is **not** about playback itself — VOD plays correctly; it is the

**`/screenshot` capture** that fails while/after protected content is on screen.

## :backhand_index_pointing_right: Additional Context

- The empty payload is `HTTP 200 {“value”: null}`, **not** an HTTP error — so it is

indistinguishable from “toolkit not enabled”, which initially sent us down the

wrong path (the `/tmp/automation-toolkit.enable` file). Log **B** rules the enable

file out (fresh touch + relaunch, still empty).

- We verified the reboot recovery path works via `vega exec vda reboot` +

`vega exec vda wait-for-device`.

- Open questions we’d appreciate answers to:

1. Is `/screenshot` empty because the feature moved to a secure/protected plane

 (HDCP/Widevine) the grabber can't read?

2. Can the toolkit return a **black frame** (or an explicit error code) for

 protected content instead of an empty \`value: null\`?

3. Why does it stay broken **after** playback ends and the app is killed, until

 reboot — is the protected plane / decoder session not torn down?

4. Is there a supported **soft reset** (via VDA / a privileged `vega exec vda …`

 command) to restore capture without a full reboot?

5. Is this expected on retail Fire TV, or specific to this build / dev unit?

Hi @Vitalii_Rurenko ,

Thank you for the detailed bug report - the logs, timeline, and root cause hypothesis are very helpful.

1. DRM screenshot blocking is by design - the Appium Commands documentation ( Appium Commands | Design and Develop Vega Apps ) states: “Don’t capture digital rights management (DRM) content” for the get_screenshot command. The automation toolkit cannot read back from a secure/protected hardware overlay plane (HDCP/Widevine), which is why pre-roll ads capture fine but the protected feature does not.

2. Returning {“value”: null} with no distinct error is not ideal — we agree that a black/placeholder frame or an explicit error code would be more helpful than a silent null. We’ll pass this feedback on
to the team.

Regarding the “stuck until reboot” behavior: Our team is investigating this issue and will provide an update as soon as we have more information.

In the meantime, your workaround (detecting the stuck state and rebooting between test sections) is the best available mitigation. Functional checks via the element tree remain unaffected.

We appreciate the quality of this report and your patience.

Thanks for helping us improve the Vega platform.

Warm regards,
Aishwarya

Hi @Vitalii_Rurenko,

Thank you for your patience on this.

To continue the investigation, we need journalctl logs from the device captured during a reproduction of the issue. Could you provide those?

In the meantime, your reboot workaround remains the best available mitigation.

Warm regards,
Aishwarya

give me some time for the logs. I do not have VEGA SDK on my current PC.

hello @amen
I’ve just sent you the logs to PM (DeviceStuck.txt).

Scenario:

  1. Run the app
  2. Run VOD playback
  3. Exit playback
  4. Stop logger.

The command used for logs: vega exec vda shell loggingctl log -f --since now