From 38618285c93a9582e5b1aad108e77f9608d02e27 Mon Sep 17 00:00:00 2001 From: PJ Date: Wed, 10 Jun 2026 18:09:59 +0530 Subject: [PATCH] fix(sidecar): retry maestro android driver startup The maestro Android driver's dadb.open() occasionally misses its startup deadline (its instrumentation host is slow to come up right after a reboot or per-run reinstall), which aborted the whole run. Retry the open a few times with a short backoff so a transient timeout recovers. --- .../dev/sanderling/sidecar/DriverBackend.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/sidecar/src/main/kotlin/dev/sanderling/sidecar/DriverBackend.kt b/sidecar/src/main/kotlin/dev/sanderling/sidecar/DriverBackend.kt index 25ba08c..36e4572 100644 --- a/sidecar/src/main/kotlin/dev/sanderling/sidecar/DriverBackend.kt +++ b/sidecar/src/main/kotlin/dev/sanderling/sidecar/DriverBackend.kt @@ -562,7 +562,27 @@ class MaestroDriverBackend(private val serial: String?) : DriverBackend { dadb = buildDadb(serial) val hostPort = java.net.ServerSocket(0).use { it.localPort } driver = maestro.drivers.AndroidDriver(dadb, hostPort) - driver.open() + openWithRetry() + } + + // openWithRetry tolerates the maestro Android driver's occasional startup + // timeout (its instrumentation host can miss the dadb.open() deadline, + // especially right after a device reboot or per-run reinstall). A transient + // failure should not abort the whole run, so retry a few times with a short + // backoff before giving up. + private fun openWithRetry() { + val attempts = 4 + for (attempt in 1..attempts) { + try { + driver.open() + return + } catch (cause: Exception) { + runCatching { driver.close() } + if (attempt == attempts) throw cause + System.err.println("android driver open failed (attempt $attempt/$attempts): ${cause.message}; retrying") + Thread.sleep(2000) + } + } } override fun launch(bundleId: String, clearState: Boolean, env: Map) {