Skip to content

[Bug]: A thread refused with DEVICE_IN_USE can still tap on the device #31303

Open

Description

@spadaval

Before submitting

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

Area

apps/server

Steps to reproduce

  1. Thread A: device_open an Android emulator, then <launcher> open com.android.settings.
  2. Thread B: device_open the same emulator, then <launcher> open com.android.settings. This fails with DEVICE_IN_USE.
  3. Thread B: <launcher> snapshot -i, then <launcher> press <x> <y>.

Expected behavior

Thread B's commands are refused while thread A holds the device.

Actual behavior

Only open is refused. B's snapshot returns A's screen, and B's press taps A's app:

$ B open com.android.settings   -> Error (DEVICE_IN_USE)                exit 1
$ B snapshot -i                 -> A's Settings screen                  exit 0
$ A session list                -> A's session and B's, same emulator
$ B press 400 860               -> Tapped (400, 860)                    exit 0

This happened for real with several threads sharing one emulator: a thread refused at open went on to tap and swipe through another thread's app.

Cause

  • T3's device_open launcher adds --platform and --serial to every command.
  • agent-device 0.21.31 runs a command for a session that never opened if it has those flags. A snapshot run that way even creates the missing session. Only open and a few commands like install and boot check who holds the device.

This is different from #17470. There, a device stays held after a thread is done. Here, a held device doesn't stop another thread.

Impact

Major degradation or frequent failure

Version or commit

main at 67eebac, agent-device 0.21.31

Environment

Linux, local Android emulator (API 35)

Screenshots, recordings, or supporting files

Before B's press After B's press

Workaround

None. The agent has to stop by itself after open is refused.

Activity

  1. vinicius741 commented on Nov 6, 2026

    @vinicius741

    Reproduced in the wild on stable 0.1.7 (agent-device 0.21.31), Linux, local Android emulator (API 36). Two Claude Code threads were involved. Thread A's claim had been stale for hours (details on #17470).

    From thread B's agent-device events.ndjson (UTC, Nov 6):

    02:32:23.621  open      error  DEVICE_IN_USE  (in use by session t3-e6e32ac5…, thread A)
    02:32:23.880  snapshot  ok     (interactiveOnly)
    02:32:30.060  press     ok     Tapped @e15 (515, 1514)
    02:32:44.239  snapshot  ok
    

    Afterwards, session list showed both sessions on emulator-5554.

    The agent ran these steps in one shell line and sent open's output to >/dev/null 2>&1. It never saw the refusal and went on to drive the emulator. The surrounding agent behavior was the bad part here, but it shows why only refusing open isn't enough: if one command's error is dropped, the session can still drive a device another thread holds. Refusing every command for a session that doesn't hold the claim, as #31304 proposes, would have stopped it.

    Posted by Claude Code (Claude Opus 5.5) on behalf of @vinicius741.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions