Skip to content

[Bug]: scrcpy connect failure leaks an unhandled promise rejection (ExactReadable ended), crashing the process (exit 1) #2836

Description

@Ginfai

Version

版本:@midscene/android 1.10.6(含 2807 的1.10.5-beta 起即存在)

Details

当 AndroidDevice.connect()(或 retryScrcpy())触发的 scrcpy 连接在初始化中途失败时,连接错误本身能被正确捕获并通过
getScrcpyStatus().lastError 暴露;但同一次失败还会额外泄漏一个未处理的 promise rejection,冒泡到进程级触发 Node 默认的
--unhandled-rejections=throw,直接以 exit code 1 杀掉进程。

结果:调用方即使正确地用 getScrcpyStatus() 检测到失败并准备重试/降级,也会在重试跑起来之前被这个逃逸的 rejection
杀死——无法在库外捕获。

逃逸 rejection 的堆栈

Error: ExactReadable ended
at BufferedReadableStream.#readSource (@yume-chan/stream-extra/src/buffered.ts:57:19)
at AdbServerStream.readOkay (@yume-chan/adb/src/server/stream.ts:59:26)
at AdbServerClient.#waitForUnchecked (@yume-chan/adb/src/server/client.ts:380:13)

根因分析(依据 dist/es/index.mjs 打包产物推断,烦请对照 src)

  • 链路:AndroidDevice.connect() → ScrcpyDeviceAdapter.initialize() → ensureManager() + manager 的 ensureConnected()(内部
    AdbScrcpyClient.start())。
  • 当 AdbScrcpyClient.start() 中途失败(如设备媒体栈未就绪 → ExactReadable ended),this.scrcpyClient = await
    AdbScrcpyClient.start(...) 的赋值未完成,this.scrcpyClient 仍为 null。
  • connect 的 catch 里执行 await this.disconnect(),但 disconnect() 读到 this.scrcpyClient === null,无法 close
    那个半初始化的 client → 它内部一个仍 pending 的读(AdbServerClient.#waitForUnchecked)成为悬垂 promise。
  • 连接错误经 createConnectionError 正常抛出并暴露;但那个悬垂读单独 reject、无人 catch → unhandledRejection → 进程 exit 1。
  • 可观测特征:同一次连接失败产生两个 ExactReadable ended——一个被捕获(getScrcpyStatus().lastError),一个裸奔逃逸。

Reproduce link

const device = new AndroidDevice(deviceId, { scrcpyConfig: { enabled: true } }); await device.connect(); // scrcpy 首建失败被吞成 warning const s = device.getScrcpyStatus(); // { enabled:true, connected:false, lastError:'...ExactReadable ended' } // 此处已正确检测失败、准备重试;但数百 ms 后 // AdbScrcpyClient.start() 遗留的悬垂读 reject → unhandledRejection → 进程 exit 1

Reproduce Steps

云手机重装系统后,媒体栈(SurfaceFlinger 显示捕获 + MediaCodec H.264 编码器)就绪晚于
sys.boot_completed,该窗口内首次 scrcpy
连接必失败,稳定触发。一次三台设备同时连接的运行中,三台各留一个悬垂 rejection、三个 worker 同时崩溃。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions