[GH-ISSUE #3886] [Bug] ADB version-probe timeout becomes a nonretryable capability error #1506

Closed
opened 2026-10-16 21:06:17 +00:00 by bsakaguchi · 4 comments
Owner

Originally created by @coygeek on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3886

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Provider or capability discovery

Observed behavior

When the ADB client-version probe exceeds its 10,000 ms deadline, the timeout is reclassified as an unsupported-configuration failure: the caller receives code: "capability_unavailable", retryable: false, with no details.timeout_ms or ETIMEDOUT diagnostic, so a stuck probe looks like an unusable adb binary rather than a timeout.

A client-version probe that exceeds its 10,000 ms deadline is converted to AdbConfigurationFailure("version_unresolved"), then to AnalysisCapabilityUnavailableError.

The public result is code: "capability_unavailable", category: "unsupported_provider", retryable: false, with the message The selected adb binary did not report a usable version., and it omits details.timeout_ms and the original ETIMEDOUT diagnostic.

A deadline expiration therefore looks like unsupported configuration rather than a timeout, and automated callers cannot recover the timeout reason from the projected diagnostics.

Reproduction

The public failure chain is established by source inspection; the same version probe precedes every ADB operation. A helper-only probe independently confirms the final projection without executing adb. Run from the built source checkout root:

nice -n 10 node --input-type=module - <<'JS'
import {AdbConfigurationFailure} from './dist/adb/AdbConfiguration.js';
import {providerConfigurationRefusal} from './dist/domain/providerConfigurationRefusal.js';
import {projectAnalysisError} from './dist/domain/analysisErrorProjection.js';
const timeout = Object.assign(new Error('Command timed out'), {
  code: 'ETIMEDOUT', killed: true, stdout: '', stderr: ''
});
const failure = new AdbConfigurationFailure(
  'version_unresolved',
  'The selected adb binary did not report a usable version',
  {selected_binary: 'adb', exit_code: 'ETIMEDOUT', stderr_excerpt: ''},
  {cause: timeout}
);
console.log(JSON.stringify(projectAnalysisError(
  providerConfigurationRefusal('adb', 'inspect_adb_client', failure)
), null, 2));
JS

Expected behavior

docs/adb-device-analysis.md:161-164 includes client probes in the invocation budgets and says stopped commands report provider_timeout.

src/domain/analysisErrorCore.ts:150-163 defines declared-deadline expiration as AnalysisTimeoutError, and src/domain/analysisErrorProjection.ts:204 maps that error to provider_timeout.

A version probe that exceeds its deadline should surface as a timeout, preserving its timeout identity and recovery information, rather than as an unsupported-configuration error.

Impact and scope

a slow or stuck version probe loses its timeout identity and recovery information for client inspection and every dependent device operation, and automated callers receive a nonretryable provider classification; it does not establish a failure for promptly responding clients.

Workaround: none. no caller option preserves the structured timeout result on this path, and the 10,000 ms probe deadline has no caller override.

Reporter's impact estimate: P2 (supported).

REA version

6.5.0, built from source at main commit 034342ba28 (latest npm release is 6.5.0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1 arm64; This is a source-only finding for the public CLI/MCP timeout path; the full version-probe timeout, the CLI timeout path, and any adb process or device operation were not executed. Only the final error-projection helpers were run.

Target and artifact context

ADB client binary and provider admission

Interface

CLI

Earliest failure stage

Setup or provider admission

Relevant output or evidence

  • Expected source: docs/adb-device-analysis.md:161-164; timeout error and projection at src/domain/analysisErrorCore.ts:150-163 and src/domain/analysisErrorProjection.ts:204
  • Failure source: reachable chain through src/adb/AdbConfiguration.ts:15 (10,000 ms deadline), src/process/ExecFileOutput.ts:37-53 (sets ETIMEDOUT on stop), src/adb/AdbConfiguration.ts:115-144 (ETIMEDOUT reaches the version_unresolved fallback), src/adb/AdbProvider.ts:700-709 and src/domain/providerConfigurationRefusal.ts:12-20 (forwards neither diagnostics nor a retryable override; default retryable is false at src/domain/analysisErrorCore.ts:134), projected at src/domain/analysisErrorProjection.ts:200; final projection reproduced by the helper probe below
  • Evidence provenance: static
  • Local verification: static-proof
  • Reproduction completeness: incomplete
  • Missing fact: a real or simulated version-probe timeout driving the full AdbProvider admission path (producing the AdbConfigurationFailure from an actual ETIMEDOUT) was not executed; only the terminal error-projection helpers were run. Runtime reproduction of the end-to-end probe timeout was not run.
{
  "code": "capability_unavailable",
  "category": "unsupported_provider",
  "message": "The selected adb binary did not report a usable version.",
  "retryable": false,
  "remediation": {
    "action": "The selected adb binary did not report a usable version."
  },
  "details": {
    "provider_id": "adb",
    "operation": "inspect_adb_client",
    "reason": "The selected adb binary did not report a usable version."
  }
}

Typed outcome or recovery expected

A version-probe deadline expiration should project as a timeout (provider_timeout) preserving the timeout_ms/ETIMEDOUT diagnostics and an appropriate retryable disposition, not a nonretryable capability_unavailable/unsupported_provider error.

Restoration check:

Failing: a version probe stopped at its deadline projects code: "capability_unavailable", category: "unsupported_provider", retryable: false, with no timeout diagnostic, as the helper probe reproduces.

Passing: the same deadline expiration projects a timeout classification (provider_timeout) that preserves the timeout reason and recovery information, consistent with the deadline contract in the cited documentation; a promptly responding client remains unaffected.

Analysis or suspected cause

The 10,000 ms probe deadline (AdbConfiguration.ts:15) stops the child with code ETIMEDOUT (ExecFileOutput.ts:37-53); AdbConfiguration.ts:115-144 only special-cases cancellation and ENOENT, so ETIMEDOUT falls through to AdbConfigurationFailure("version_unresolved"); providerConfigurationRefusal (providerConfigurationRefusal.ts:12-20) forwards neither diagnostics nor a retryable override, defaulting to retryable false (analysisErrorCore.ts:134) and projecting to capability_unavailable (analysisErrorProjection.ts:200).

Additional context

The 10,000 ms probe deadline is a source constant at src/adb/AdbConfiguration.ts:15 with no caller override on this path, so the misclassification affects client inspection and every device operation that performs this prerequisite.

Related issues: closed #3347 is the equivalent Apktool-specific version-probe timeout bug and is a separate provider path, not claimed as a regression here; closed #1952 concerns different ADB configuration and refusal failures; open #3369 concerns checksum-response overflow during ADB push. None covers ADB version-probe timeout classification.

This report came from AI-assisted source review with local reproduction where stated above; the evidence labels distinguish observed runs from source-only reasoning.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3886 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Provider or capability discovery ### Observed behavior When the ADB client-version probe exceeds its 10,000 ms deadline, the timeout is reclassified as an unsupported-configuration failure: the caller receives `code: "capability_unavailable"`, `retryable: false`, with no `details.timeout_ms` or `ETIMEDOUT` diagnostic, so a stuck probe looks like an unusable adb binary rather than a timeout. A client-version probe that exceeds its 10,000 ms deadline is converted to `AdbConfigurationFailure("version_unresolved")`, then to `AnalysisCapabilityUnavailableError`. The public result is `code: "capability_unavailable"`, `category: "unsupported_provider"`, `retryable: false`, with the message `The selected adb binary did not report a usable version.`, and it omits `details.timeout_ms` and the original `ETIMEDOUT` diagnostic. A deadline expiration therefore looks like unsupported configuration rather than a timeout, and automated callers cannot recover the timeout reason from the projected diagnostics. ### Reproduction The public failure chain is established by source inspection; the same version probe precedes every ADB operation. A helper-only probe independently confirms the final projection without executing adb. Run from the built source checkout root: ```sh nice -n 10 node --input-type=module - <<'JS' import {AdbConfigurationFailure} from './dist/adb/AdbConfiguration.js'; import {providerConfigurationRefusal} from './dist/domain/providerConfigurationRefusal.js'; import {projectAnalysisError} from './dist/domain/analysisErrorProjection.js'; const timeout = Object.assign(new Error('Command timed out'), { code: 'ETIMEDOUT', killed: true, stdout: '', stderr: '' }); const failure = new AdbConfigurationFailure( 'version_unresolved', 'The selected adb binary did not report a usable version', {selected_binary: 'adb', exit_code: 'ETIMEDOUT', stderr_excerpt: ''}, {cause: timeout} ); console.log(JSON.stringify(projectAnalysisError( providerConfigurationRefusal('adb', 'inspect_adb_client', failure) ), null, 2)); JS ``` ### Expected behavior docs/adb-device-analysis.md:161-164 includes client probes in the invocation budgets and says stopped commands report `provider_timeout`. src/domain/analysisErrorCore.ts:150-163 defines declared-deadline expiration as `AnalysisTimeoutError`, and src/domain/analysisErrorProjection.ts:204 maps that error to `provider_timeout`. A version probe that exceeds its deadline should surface as a timeout, preserving its timeout identity and recovery information, rather than as an unsupported-configuration error. ### Impact and scope a slow or stuck version probe loses its timeout identity and recovery information for client inspection and every dependent device operation, and automated callers receive a nonretryable provider classification; it does not establish a failure for promptly responding clients. Workaround: none. no caller option preserves the structured timeout result on this path, and the 10,000 ms probe deadline has no caller override. Reporter's impact estimate: P2 (supported). ### REA version 6.5.0, built from source at main commit 034342ba28c8e56c3746be92b78aa7299afa02bd (latest npm release is 6.5.0) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1 arm64; This is a source-only finding for the public CLI/MCP timeout path; the full version-probe timeout, the CLI timeout path, and any adb process or device operation were not executed. Only the final error-projection helpers were run. ### Target and artifact context ADB client binary and provider admission ### Interface CLI ### Earliest failure stage Setup or provider admission ### Relevant output or evidence - Expected source: docs/adb-device-analysis.md:161-164; timeout error and projection at src/domain/analysisErrorCore.ts:150-163 and src/domain/analysisErrorProjection.ts:204 - Failure source: reachable chain through src/adb/AdbConfiguration.ts:15 (10,000 ms deadline), src/process/ExecFileOutput.ts:37-53 (sets `ETIMEDOUT` on stop), src/adb/AdbConfiguration.ts:115-144 (ETIMEDOUT reaches the `version_unresolved` fallback), src/adb/AdbProvider.ts:700-709 and src/domain/providerConfigurationRefusal.ts:12-20 (forwards neither diagnostics nor a retryable override; default retryable is false at src/domain/analysisErrorCore.ts:134), projected at src/domain/analysisErrorProjection.ts:200; final projection reproduced by the helper probe below - Evidence provenance: static - Local verification: static-proof - Reproduction completeness: incomplete - Missing fact: a real or simulated version-probe timeout driving the full `AdbProvider` admission path (producing the `AdbConfigurationFailure` from an actual `ETIMEDOUT`) was not executed; only the terminal error-projection helpers were run. Runtime reproduction of the end-to-end probe timeout was not run. ```json { "code": "capability_unavailable", "category": "unsupported_provider", "message": "The selected adb binary did not report a usable version.", "retryable": false, "remediation": { "action": "The selected adb binary did not report a usable version." }, "details": { "provider_id": "adb", "operation": "inspect_adb_client", "reason": "The selected adb binary did not report a usable version." } } ``` ### Typed outcome or recovery expected A version-probe deadline expiration should project as a timeout (provider_timeout) preserving the timeout_ms/ETIMEDOUT diagnostics and an appropriate retryable disposition, not a nonretryable capability_unavailable/unsupported_provider error. Restoration check: Failing: a version probe stopped at its deadline projects `code: "capability_unavailable"`, `category: "unsupported_provider"`, `retryable: false`, with no timeout diagnostic, as the helper probe reproduces. Passing: the same deadline expiration projects a timeout classification (`provider_timeout`) that preserves the timeout reason and recovery information, consistent with the deadline contract in the cited documentation; a promptly responding client remains unaffected. ### Analysis or suspected cause The 10,000 ms probe deadline (AdbConfiguration.ts:15) stops the child with code ETIMEDOUT (ExecFileOutput.ts:37-53); AdbConfiguration.ts:115-144 only special-cases cancellation and ENOENT, so ETIMEDOUT falls through to AdbConfigurationFailure("version_unresolved"); providerConfigurationRefusal (providerConfigurationRefusal.ts:12-20) forwards neither diagnostics nor a retryable override, defaulting to retryable false (analysisErrorCore.ts:134) and projecting to capability_unavailable (analysisErrorProjection.ts:200). ### Additional context The 10,000 ms probe deadline is a source constant at src/adb/AdbConfiguration.ts:15 with no caller override on this path, so the misclassification affects client inspection and every device operation that performs this prerequisite. Related issues: closed #3347 is the equivalent Apktool-specific version-probe timeout bug and is a separate provider path, not claimed as a regression here; closed #1952 concerns different ADB configuration and refusal failures; open #3369 concerns checksum-response overflow during ADB push. None covers ADB version-probe timeout classification. This report came from AI-assisted source review with local reproduction where stated above; the evidence labels distinguish observed runs from source-only reasoning.
Author
Owner

@XinwangTao commented on GitHub (Oct 15, 2026):

Agreed — conflating a probe timeout with an unsupported adb binary is misleading for callers and breaks retry behavior. A stuck adb (device enumeration or a slow shell on a busy host) is transient and worth retrying, whereas capability_unavailable is treated as permanent configuration failure; losing details.timeout_ms and the ETIMEDOUT code makes it impossible to tell the two apart programmatically. The version probe should classify deadline expiration as a retryable timeout error that preserves the original diagnostic, and only genuine probe failures (adb missing, bad format) should project to capability_unavailable.

<!-- gh-comment-id:6185702457 --> @XinwangTao commented on GitHub (Oct 15, 2026): Agreed — conflating a probe timeout with an unsupported adb binary is misleading for callers and breaks retry behavior. A stuck adb (device enumeration or a slow shell on a busy host) is transient and worth retrying, whereas capability_unavailable is treated as permanent configuration failure; losing details.timeout_ms and the ETIMEDOUT code makes it impossible to tell the two apart programmatically. The version probe should classify deadline expiration as a retryable timeout error that preserves the original diagnostic, and only genuine probe failures (adb missing, bad format) should project to capability_unavailable.
Author
Owner

@yankikucuk commented on GitHub (Oct 15, 2026):

Working on this. Confirmed from the probe path: the ETIMEDOUT stop fell through to version_unresolved and projected as capability_unavailable, retryable: false, with no deadline diagnostics. inspectAdbClient now raises the typed AnalysisTimeoutError (selected binary and stderr excerpt as diagnostics), the provider returns it as provider_timeout with details.timeout_ms: 10000, and the readiness probe reports the client unavailable for now instead of failing. Cancellation, missing-binary and unrecognised-output cases are unchanged. Draft PR follows shortly.

<!-- gh-comment-id:6191505407 --> @yankikucuk commented on GitHub (Oct 15, 2026): Working on this. Confirmed from the probe path: the `ETIMEDOUT` stop fell through to `version_unresolved` and projected as `capability_unavailable`, `retryable: false`, with no deadline diagnostics. `inspectAdbClient` now raises the typed `AnalysisTimeoutError` (selected binary and stderr excerpt as diagnostics), the provider returns it as `provider_timeout` with `details.timeout_ms: 10000`, and the readiness probe reports the client unavailable for now instead of failing. Cancellation, missing-binary and unrecognised-output cases are unchanged. Draft PR follows shortly.
Author
Owner

@XinwangTao commented on GitHub (Oct 16, 2026):

yankikucuk, mapping the deadline to a typed provider_timeout with timeout_ms preserved and keeping capability_unavailable for genuine probe failures is exactly the separation callers need — retry stays possible on transient adb stalls while permanent misconfiguration still surfaces distinctly. Good that cancellation and missing-binary cases are unchanged.

<!-- gh-comment-id:6208780492 --> @XinwangTao commented on GitHub (Oct 16, 2026): yankikucuk, mapping the deadline to a typed provider_timeout with timeout_ms preserved and keeping capability_unavailable for genuine probe failures is exactly the separation callers need — retry stays possible on transient adb stalls while permanent misconfiguration still surfaces distinctly. Good that cancellation and missing-binary cases are unchanged.
Author
Owner

@coygeek commented on GitHub (Oct 18, 2026):

Addressed on main by github.com/morluto/rea@0d22474280. The ADB version probe now classifies ETIMEDOUT as AnalysisTimeoutError and preserves it through provider admission, yielding retryable provider_timeout with timeout_ms 10000 instead of capability_unavailable. Regression tests cover provider admission and the public CLI.

I checked the current source and relevant regression coverage; I have not rerun the original runtime reproduction in this review. Closing as completed.

<!-- gh-comment-id:6244128976 --> @coygeek commented on GitHub (Oct 18, 2026): Addressed on main by https://github.com/morluto/rea/commit/0d224742808e32106e2ee93340c6755ee5e0a0db. The ADB version probe now classifies ETIMEDOUT as AnalysisTimeoutError and preserves it through provider admission, yielding retryable provider_timeout with timeout_ms 10000 instead of capability_unavailable. Regression tests cover provider admission and the public CLI. I checked the current source and relevant regression coverage; I have not rerun the original runtime reproduction in this review. Closing as completed.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1506
No description provided.