[GH-ISSUE #6040] [Bug] LLDB discovery timeouts are reported as a missing installation #3686

Closed
opened 2026-10-31 00:43:04 +00:00 by bsakaguchi · 1 comment
Owner

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

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

An LLDB resolver timeout is returned from observe_native_calls as capability_unavailable, category unsupported_provider, retryable: false, with message and remediation LLDB from Xcode or the Command Line Tools is not available. The result has no inspect_timeout_state recovery. Resolver output-limit and io failures receive the same missing-installation error.

Executed production-provider cases reproduced these mappings using injected resolver failures. A true unavailable control returned the same message correctly; a separate AnalysisTimeoutError projection control produced provider_timeout and the expected recovery. No real 60-second xcrun stall was induced.

Reproduction

The following cases are extracted from an executed eight-case focused test. Save the code as tests/boundary/providers/native/lldbDiscovery.repro.test.ts in the prepared source checkout and run with the repository-pinned Node:

node node_modules/vitest/vitest.mjs run --project boundary tests/boundary/providers/native/lldbDiscovery.repro.test.ts --maxWorkers=1 --no-file-parallelism

The assertions demonstrate current behavior. The test exercises the production provider client and target digest preflight against the existing macOS /usr/bin/true, while injecting only the tool resolver result. It never launches LLDB or the target. Client closure succeeds for every case.

import { expect, it } from 'vitest';
import { AnalysisCancelledError, AnalysisTimeoutError } from '../../../../src/domain/analysisErrorCore.js';
import { projectAnalysisError } from '../../../../src/domain/analysisErrorProjection.js';
import { err } from '../../../../src/domain/result.js';
import { LldbCallTracer } from '../../../../src/native/LldbCallTracer.js';
import { NativeCommandFailure, NATIVE_COMMAND_TIMEOUT_MS } from '../../../../src/native/CommandRunner.js';
import { NativeMacOSProvider } from '../../../../src/native/NativeMacOSProvider.js';
import { NativeFixtureRunner, nativeMachoTargetForFile } from '../../../fixtures/nativeCommands.js';

const input = { breakpoints: [{ kind: 'function', name: 'main' }] };
async function run(reason: 'cancelled' | 'timeout' | 'output-limit' | 'io' | 'unavailable', callerReason?: unknown) {
  const controller = new AbortController();
  let launches = 0;
  let resolutions = 0;
  const failure = new NativeCommandFailure('lldb', reason, null, {
    cause: new Error('controlled resolver failure'),
    ...(reason === 'cancelled' ? { cancellation: { reason: callerReason } } : {}),
  });
  const tracer = new LldbCallTracer({}, async () => {
    launches++;
    throw new Error('LLDB must not launch');
  }, async (tool, signal) => {
    expect(tool).toBe('lldb');
    expect(signal).toBe(controller.signal);
    resolutions++;
    if (reason === 'cancelled') controller.abort(callerReason);
    return err(failure);
  });
  const client = new NativeMacOSProvider({}, new NativeFixtureRunner(), 'darwin', () => tracer)
    .createClient(await nativeMachoTargetForFile('/usr/bin/true'));
  try {
    const result = await client.execute('observe_native_calls', input, { signal: controller.signal });
    expect(resolutions).toBe(1);
    expect(launches).toBe(0);
    expect(result.ok).toBe(false);
    if (result.ok) throw new Error('expected discovery failure');
    return { error: result.error, projection: projectAnalysisError(result.error), failure };
  } finally {
    expect((await client.close()).ok).toBe(true);
  }
}
it.each(['timeout', 'output-limit', 'io', 'unavailable'] as const)('classifies discovery %s as missing LLDB', async (reason) => {
  const result = await run(reason);
  expect(result.projection).toMatchObject({
    code: 'capability_unavailable', category: 'unsupported_provider', retryable: false,
    message: 'LLDB from Xcode or the Command Line Tools is not available',
  });
  expect(result.projection.recovery).toBeUndefined();
  expect(result.error.cause).toBeUndefined();
  console.log(reason, JSON.stringify(result.projection));
});
it('timeout projection control', () => {
  expect(projectAnalysisError(new AnalysisTimeoutError('observe_native_calls', NATIVE_COMMAND_TIMEOUT_MS)))
    .toMatchObject({ code: 'provider_timeout', category: 'timeout', recovery: {
      kind: 'inspect_timeout_state', operation: 'observe_native_calls', timeout_ms: 60000,
    }});
});

The four resolver classification cases and timeout projection control passed their current-behavior assertions. A second run asserting provider_timeout, category timeout and inspect_timeout_state for the timeout resolver case failed: it received capability_unavailable and unsupported_provider, without recovery.

Expected behavior

Preserve the resolver failure class. A command-owner deadline should become AnalysisTimeoutError with the native-command budget of 60000 ms and recovery.kind: "inspect_timeout_state". The deadline contract assigns provider_timeout to REA's server/provider deadlines. Output-budget and I/O failures should retain their own failure class instead of claiming LLDB is absent. Genuine unavailability should keep the current requirement message.

Impact and scope

P2: false missing-installation diagnostics and omission of timeout recovery can send callers toward unrelated toolchain repairs instead of examining the timed-out discovery. The result also loses the injected physical cause. This is bounded to discovery failures; ordinary installed-tool discovery was not tested. Callers can independently inspect host toolchain state and retry the command, but successful recovery from an actual stalled xcrun process was not measured.

No target launch, data corruption, surviving process or cleanup failure was demonstrated. Natural frequency of real discovery timeouts and I/O failures is unknown.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

macOS 27.0.1 arm64; Node.js v24.18.0; repository-locked Vitest 5.0.3. Resolver failures were simulated; no debugger was started.

Target and artifact context

Existing macOS /usr/bin/true, with real file digest validation and fixture Mach-O metadata. No executable or target process was launched and no binary was modified.

Interface

Other

Earliest failure stage

Setup or provider admission

Relevant output or evidence

The timeout, output-limit and I/O cases each produced these reduced fields:

{"code":"capability_unavailable","category":"unsupported_provider","message":"LLDB from Xcode or the Command Line Tools is not available","retryable":false,"remediation":{"action":"LLDB from Xcode or the Command Line Tools is not available"}}

Each returned error had an undefined cause and no recovery field. The timeout projection control returned provider_timeout, category timeout, and {"kind":"inspect_timeout_state","operation":"observe_native_calls","timeout_ms":60000}. All cases made zero debugger launches and closed the provider client successfully.

The registered CLI command calls observe_native_calls; the native provider dispatches to the shared observation workflow. That public reachability is source-derived; the executed test enters at the production provider client, without CLI or MCP transport.

Typed outcome or recovery expected

Return provider_timeout with the actual command budget and timeout recovery for resolver timeout; preserve output-budget and physical I/O failure classifications for the other reasons. Keep actual captured diagnostics and independent cleanup authority when present. Cleanup-owner retention was not exercised by these cases.

Analysis or suspected cause

The discovery failure branch distinguishes only cancellation from all other failures. The xcrun resolver retains timeout, output-limit and I/O reasons, while the owned command invocation enforces the 60000 ms deadline. The native command translator already distinguishes these failure types for other native operations. The timeout projection supplies timeout recovery when the error type is retained.

Additional context

#5913 and its additional cleanup cases concern completed traces after launch; #5385 concerns cancellation replacing an observed physical exit failure. #5287 fixed native cancellation provenance and late resolver I/O classification, while this report concerns the later LLDB consumer replacing the resolver's correct classification. PR #6014 explicitly excludes native deadlines; PR #4751 addresses readback descriptor cleanup. None changes this discovery mapping.

Fetched upstream main 6d356b84344659085f2b912857b5e27fe0160c9b has identical tracer, resolver and provider translator. Its projection changes only an unrelated captured-output policy, leaving the timeout/capability mapping unchanged. That newer revision was source-compared, not executed.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6040 ### 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 An LLDB resolver `timeout` is returned from `observe_native_calls` as `capability_unavailable`, category `unsupported_provider`, `retryable: false`, with message and remediation `LLDB from Xcode or the Command Line Tools is not available`. The result has no `inspect_timeout_state` recovery. Resolver `output-limit` and `io` failures receive the same missing-installation error. Executed production-provider cases reproduced these mappings using injected resolver failures. A true `unavailable` control returned the same message correctly; a separate `AnalysisTimeoutError` projection control produced `provider_timeout` and the expected recovery. No real 60-second xcrun stall was induced. ### Reproduction The following cases are extracted from an executed eight-case focused test. Save the code as `tests/boundary/providers/native/lldbDiscovery.repro.test.ts` in the prepared source checkout and run with the repository-pinned Node: ```sh node node_modules/vitest/vitest.mjs run --project boundary tests/boundary/providers/native/lldbDiscovery.repro.test.ts --maxWorkers=1 --no-file-parallelism ``` The assertions demonstrate current behavior. The test exercises the production provider client and target digest preflight against the existing macOS `/usr/bin/true`, while injecting only the tool resolver result. It never launches LLDB or the target. Client closure succeeds for every case. ```ts import { expect, it } from 'vitest'; import { AnalysisCancelledError, AnalysisTimeoutError } from '../../../../src/domain/analysisErrorCore.js'; import { projectAnalysisError } from '../../../../src/domain/analysisErrorProjection.js'; import { err } from '../../../../src/domain/result.js'; import { LldbCallTracer } from '../../../../src/native/LldbCallTracer.js'; import { NativeCommandFailure, NATIVE_COMMAND_TIMEOUT_MS } from '../../../../src/native/CommandRunner.js'; import { NativeMacOSProvider } from '../../../../src/native/NativeMacOSProvider.js'; import { NativeFixtureRunner, nativeMachoTargetForFile } from '../../../fixtures/nativeCommands.js'; const input = { breakpoints: [{ kind: 'function', name: 'main' }] }; async function run(reason: 'cancelled' | 'timeout' | 'output-limit' | 'io' | 'unavailable', callerReason?: unknown) { const controller = new AbortController(); let launches = 0; let resolutions = 0; const failure = new NativeCommandFailure('lldb', reason, null, { cause: new Error('controlled resolver failure'), ...(reason === 'cancelled' ? { cancellation: { reason: callerReason } } : {}), }); const tracer = new LldbCallTracer({}, async () => { launches++; throw new Error('LLDB must not launch'); }, async (tool, signal) => { expect(tool).toBe('lldb'); expect(signal).toBe(controller.signal); resolutions++; if (reason === 'cancelled') controller.abort(callerReason); return err(failure); }); const client = new NativeMacOSProvider({}, new NativeFixtureRunner(), 'darwin', () => tracer) .createClient(await nativeMachoTargetForFile('/usr/bin/true')); try { const result = await client.execute('observe_native_calls', input, { signal: controller.signal }); expect(resolutions).toBe(1); expect(launches).toBe(0); expect(result.ok).toBe(false); if (result.ok) throw new Error('expected discovery failure'); return { error: result.error, projection: projectAnalysisError(result.error), failure }; } finally { expect((await client.close()).ok).toBe(true); } } it.each(['timeout', 'output-limit', 'io', 'unavailable'] as const)('classifies discovery %s as missing LLDB', async (reason) => { const result = await run(reason); expect(result.projection).toMatchObject({ code: 'capability_unavailable', category: 'unsupported_provider', retryable: false, message: 'LLDB from Xcode or the Command Line Tools is not available', }); expect(result.projection.recovery).toBeUndefined(); expect(result.error.cause).toBeUndefined(); console.log(reason, JSON.stringify(result.projection)); }); it('timeout projection control', () => { expect(projectAnalysisError(new AnalysisTimeoutError('observe_native_calls', NATIVE_COMMAND_TIMEOUT_MS))) .toMatchObject({ code: 'provider_timeout', category: 'timeout', recovery: { kind: 'inspect_timeout_state', operation: 'observe_native_calls', timeout_ms: 60000, }}); }); ``` The four resolver classification cases and timeout projection control passed their current-behavior assertions. A second run asserting `provider_timeout`, category `timeout` and `inspect_timeout_state` for the timeout resolver case failed: it received `capability_unavailable` and `unsupported_provider`, without recovery. ### Expected behavior Preserve the resolver failure class. A command-owner deadline should become `AnalysisTimeoutError` with the native-command budget of 60000 ms and `recovery.kind: "inspect_timeout_state"`. The [deadline contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/mcp-contracts.md#L243-L250) assigns `provider_timeout` to REA's server/provider deadlines. Output-budget and I/O failures should retain their own failure class instead of claiming LLDB is absent. Genuine unavailability should keep the current requirement message. ### Impact and scope P2: false missing-installation diagnostics and omission of timeout recovery can send callers toward unrelated toolchain repairs instead of examining the timed-out discovery. The result also loses the injected physical cause. This is bounded to discovery failures; ordinary installed-tool discovery was not tested. Callers can independently inspect host toolchain state and retry the command, but successful recovery from an actual stalled xcrun process was not measured. No target launch, data corruption, surviving process or cleanup failure was demonstrated. Natural frequency of real discovery timeouts and I/O failures is unknown. ### REA version rea-agents 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform macOS 27.0.1 arm64; Node.js v24.18.0; repository-locked Vitest 5.0.3. Resolver failures were simulated; no debugger was started. ### Target and artifact context Existing macOS `/usr/bin/true`, with real file digest validation and fixture Mach-O metadata. No executable or target process was launched and no binary was modified. ### Interface Other ### Earliest failure stage Setup or provider admission ### Relevant output or evidence The timeout, output-limit and I/O cases each produced these reduced fields: ```json {"code":"capability_unavailable","category":"unsupported_provider","message":"LLDB from Xcode or the Command Line Tools is not available","retryable":false,"remediation":{"action":"LLDB from Xcode or the Command Line Tools is not available"}} ``` Each returned error had an undefined cause and no recovery field. The timeout projection control returned `provider_timeout`, category `timeout`, and `{"kind":"inspect_timeout_state","operation":"observe_native_calls","timeout_ms":60000}`. All cases made zero debugger launches and closed the provider client successfully. The [registered CLI command](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/cli/nativeCallCommands.ts#L16-L70) calls `observe_native_calls`; the [native provider](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/NativeMacOSProvider.ts#L316-L333) dispatches to the [shared observation workflow](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/NativeCallObservation.ts#L54-L88). That public reachability is source-derived; the executed test enters at the production provider client, without CLI or MCP transport. ### Typed outcome or recovery expected Return `provider_timeout` with the actual command budget and timeout recovery for resolver timeout; preserve output-budget and physical I/O failure classifications for the other reasons. Keep actual captured diagnostics and independent cleanup authority when present. Cleanup-owner retention was not exercised by these cases. ### Analysis or suspected cause The [discovery failure branch](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/LldbCallTracer.ts#L213-L224) distinguishes only cancellation from all other failures. The [xcrun resolver](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/CommandRunner.ts#L361-L406) retains timeout, output-limit and I/O reasons, while the [owned command invocation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/CommandRunner.ts#L505-L523) enforces the 60000 ms deadline. The [native command translator](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/native/NativeMacOSProvider.ts#L1327-L1365) already distinguishes these failure types for other native operations. The [timeout projection](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorProjection.ts#L126-L140) supplies timeout recovery when the error type is retained. ### Additional context #5913 and its additional cleanup cases concern completed traces after launch; #5385 concerns cancellation replacing an observed physical exit failure. #5287 fixed native cancellation provenance and late resolver I/O classification, while this report concerns the later LLDB consumer replacing the resolver's correct classification. PR #6014 explicitly excludes native deadlines; PR #4751 addresses readback descriptor cleanup. None changes this discovery mapping. Fetched upstream main `6d356b84344659085f2b912857b5e27fe0160c9b` has identical tracer, resolver and provider translator. Its projection changes only an unrelated captured-output policy, leaving the timeout/capability mapping unchanged. That newer revision was source-compared, not executed.
Author
Owner

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

Checked the reported fix on main at d87e73f2db.

LLDB discovery now uses translateNativeCommandFailure with the native command deadline and an LLDB-specific timeout explanation, preserving resolver cleanup dependencies. The production native-provider client regression injects timeout, output-limit, io, unavailable and cancelled resolver results; it checks provider_timeout plus inspect_timeout_state/60000 ms, resource_constraint, execution_failure and the true missing-tool message only for unavailable.

Implementation: LldbCallTracer.ts, lldbDiscovery.test.ts.

The relevant regression checks in lldbDiscovery.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380766809 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. LLDB discovery now uses translateNativeCommandFailure with the native command deadline and an LLDB-specific timeout explanation, preserving resolver cleanup dependencies. The production native-provider client regression injects timeout, output-limit, io, unavailable and cancelled resolver results; it checks provider_timeout plus inspect_timeout_state/60000 ms, resource_constraint, execution_failure and the true missing-tool message only for unavailable. Implementation: [LldbCallTracer.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/native/LldbCallTracer.ts#L517), [lldbDiscovery.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/providers/native/lldbDiscovery.test.ts#L25). The relevant regression checks in [lldbDiscovery.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/providers/native/lldbDiscovery.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3686
No description provided.