From 2a61deafc632079d10c572bc09e99233826516a3 Mon Sep 17 00:00:00 2001 From: Jin Date: Tue, 13 Oct 2026 08:56:41 -0400 Subject: [PATCH] fix(adb): report the device state when a push cannot probe the path (#2354) A push without overwrite read any unclassified failure of its existence probe as an existing path, so an offline or unauthorized device was reported as "the device path already exists". With overwrite it pushed after a probe that had not established anything. The probe's own refusal is now returned in both cases. Co-authored-by: Claude Opus 5.5 --- src/adb/AdbProvider.test.ts | 30 ++++++++++++++++++++++++++++++ src/adb/AdbProvider.ts | 18 ++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/adb/AdbProvider.test.ts b/src/adb/AdbProvider.test.ts index ae81353c7..23645e456 100644 --- a/src/adb/AdbProvider.test.ts +++ b/src/adb/AdbProvider.test.ts @@ -810,6 +810,36 @@ exit 1`, expect(result.error.message).toContain("overwrite"); }); +it.each([ + ["offline", false, "error: device offline"], + ["unauthorized", false, "error: device unauthorized"], + ["offline", true, "error: device offline"], +] as const)( + "reports the %s device state when the push existence probe cannot run (overwrite %s)", + async (state, overwrite, stderr) => { + const { binary, root } = await writeAdbStub( + (stubRoot) => `if [ "$1" = "version" ]; then ${VERSION_BLOCK}; exit 0; fi +if [ "$1" = "-s" ]; then printf '%s\\n' "$3" >> "${stubRoot}/calls"; printf '%s\\n' "${stderr}" >&2; exit 1; fi`, + ); + const localFile = join(root, "payload.bin"); + await writeFile(localFile, "x"); + const projected = await refusedBy(providerOver(binary), { + operation: "push_adb_file", + input: { + serial: "emulator-5554", + local_path: localFile, + device_path: "/data/local/tmp/payload.bin", + overwrite, + }, + }); + // Existence is unknown, so nothing is claimed about the path and no push runs. + expect(projected).toMatchObject({ code: "unsupported_target" }); + expect(projected.message).toContain(state); + expect(projected.message).not.toContain("already exists"); + expect(await readFile(join(root, "calls"), "utf8")).toBe("shell\n"); + }, +); + it("reports a local push source that does not exist", async () => { const { binary } = await writeAdbStub( () => `if [ "$1" = "version" ]; then ${VERSION_BLOCK}; exit 0; fi diff --git a/src/adb/AdbProvider.ts b/src/adb/AdbProvider.ts index b9af6d14f..d26345eba 100644 --- a/src/adb/AdbProvider.ts +++ b/src/adb/AdbProvider.ts @@ -1623,17 +1623,15 @@ export class AdbProvider { { timeoutMs: SETTINGS_TIMEOUT_MS, maxOutputBytes: 1024 }, signal, ); - let deviceExists: boolean; - if (existsProbe.ok) deviceExists = true; - else if (existsProbe.error instanceof AnalysisProtocolError) - deviceExists = false; - else if (existsProbe.error instanceof AnalysisTimeoutError) + // `test -e` exits nonzero with no adb diagnosis when the path is absent. + // Any other probe failure leaves existence unknown, so it is reported as + // is rather than read as an existing path or followed by a push. + if ( + !existsProbe.ok && + !(existsProbe.error instanceof AnalysisProtocolError) + ) return existsProbe; - else if (existsProbe.error instanceof AnalysisCancelledError) - return existsProbe; - else if (existsProbe.error instanceof AnalysisResourceConstraintError) - return existsProbe; - else deviceExists = true; // authorization/refusal states surface on push instead + const deviceExists = existsProbe.ok; if (deviceExists && !input.overwrite) return err( new AnalysisOutputError(