[GH-ISSUE #7572] [Bug] Empty trace-native-values procedure loses its field diagnostic #4510

Closed
opened 2026-10-31 00:50:24 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

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

Area

CLI

Observed behavior

For the registered CLI request trace-native-values <Mach-O> '' --json, a controlled fixture session reached the request's shared validation and returned invalid_request / invalid_input with no details member. The message only says to check the arguments, and recovery says to correct “the listed fields,” without naming a field. The canonical validator independently identified procedure with minimum length 1.

The built command's --schema --json output advertises procedure as a required string without minLength, so that discovery surface does not reveal the rejected constraint either.

The controlled replay used the production command registration, target inspection, one-shot analysis, session, validation and projection, with only the session factory replaced by the repository's fixture provider. It ran no external engine. This is not a live Hopper/Ghidra dispatcher reproduction. The fixture client was created and received health before the refusal; validation therefore does not precede provider startup.

Reproduction

  1. Use the baseline's locked dependencies and built runtime with Node.js v24.22.1. Start at the repository root with that Node first on PATH. Create a copied Mach-O and inspect the real command schema:
WORK=$(mktemp -d)
mkdir -p "$WORK/home" "$WORK/config" "$WORK/cache" "$WORK/data" "$WORK/state" "$WORK/tmp"
cp /usr/bin/true "$WORK/target"
node scripts/rea.mjs trace-native-values --schema --json
  1. Run this inline controlled replay. It registers the real CLI command and injects an execution-free fixture session. Assertions check the refusal, the missing diagnostic, and the independently available field constraint. The probe captures the command's exit status and resets its own status after verifying it.
env -i PATH="$PATH" HOME="$WORK/home" \
  XDG_CONFIG_HOME="$WORK/config" XDG_CACHE_HOME="$WORK/cache" \
  XDG_DATA_HOME="$WORK/data" XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/tmp" \
  TRACE_FIXTURE="$WORK/target" node --import tsx --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { pathToFileURL } from 'node:url';
import { resolve } from 'node:path';
const load = p => import(pathToFileURL(resolve(p)).href);
const { Cli } = await load('node_modules/incur/dist/index.js');
const { registerCoreNativeCommands } = await load('src/cli/coreNativeCommands.ts');
const { runDirectAnalysis } = await load('src/application/DirectAnalysis.ts');
const { parseConfig } = await load('src/config/parseConfig.ts');
const { silentLogger } = await load('src/logger.ts');
const { createTestBinarySession } = await load('tests/fixtures/binarySession.ts');
const { observed } = await load('tests/fixtures/analysisExecution.ts');
const { ok } = await load('src/domain/result.ts');
const { nativeValueTraceInputSchema } = await load('src/domain/native/nativeValueTrace.ts');
const { projectInputIssues } = await load('src/domain/inputIssueProjection.ts');
const events = [];
const session = createTestBinarySession(() => {
  events.push('fixture-client-created');
  return {
    execute: async operation => {
      events.push(operation);
      assert.equal(operation, 'health');
      return observed(null);
    },
    close: async () => { events.push('close'); return ok(null); },
  };
});
const forbidden = () => { throw new Error('Unexpected session factory'); };
const dependencies = {
  readConfiguration: () => parseConfig({}), createBinarySession: () => session,
  createNativeDispatchBinarySession: forbidden, createArtifactBinarySession: forbidden,
  createNativeBinarySession: forbidden, createManagedBinarySession: forbidden,
};
const cli = Cli.create({ name: 'rea' });
registerCoreNativeCommands(cli, silentLogger,
  (...args) => runDirectAnalysis(dependencies, ...args));
let stdout = '';
await cli.serve(['trace-native-values', process.env.TRACE_FIXTURE, '', '--json'], {
  env: {}, stdout: text => { stdout += text; }, exit: () => {},
});
const error = JSON.parse(stdout), commandExit = process.exitCode;
process.exitCode = 0;
assert.equal(commandExit, 1);
assert.equal(error.code, 'invalid_request');
assert.equal(error.details, undefined);
assert.deepEqual(events, ['fixture-client-created', 'health', 'close']);
const parsed = nativeValueTraceInputSchema.safeParse({procedure: ''});
assert.equal(parsed.success, false);
const issues = projectInputIssues(parsed.error.issues, {procedure: ''});
assert.deepEqual(issues, [{path: ['procedure'], reason: 'out_of_range', minimum: 1}]);
console.log(JSON.stringify({commandExit, error, events, issues}));
JS

The inline replay was executed three times, including one execution of the complete reproduction block; all three runs passed every assertion and produced the same missing-field diagnostic. No checkout changes or graphical provider are required. Do not replace the fixture replay with a default-provider invocation on a valid target when avoiding provider startup is required.

Expected behavior

An expected input rejection should retain the caller field and safe constraint, so the caller can identify the correction: procedure must have at least one character. The CLI's discovery schema should express that same nonempty semantic field. The current rejection code and failure exit status can remain.

The project's input-error policy explicitly requires caller field/index paths and safe constraints; its CLI translation policy requires canonical semantic fields and schema/parser checks for empty strings. The exact contract references are listed under Evidence.

Impact and scope

P3: malformed input remains refused, but its correction diagnostic omits the rejected field and bound, and CLI schema discovery omits that bound too. A caller can supply a nonempty procedure instead. No valid-analysis failure, incorrect graph, data loss, or actual failed agent recovery was demonstrated.

Observed runtime scope is the controlled CLI fixture replay, three times. Live provider behavior, MCP admission, other commands and other platforms were not exercised. The separate real dispatcher control with an absent target and the same empty procedure returned target_unavailable; target access can mask this later input failure.

REA version

Exact node scripts/rea.mjs --version stdout:

6.8.0

Source commit: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1, build 26A434; arm64. Built CLI runtime and locked dependencies at the stated baseline. The supplemental replay uses the repository's fixture provider, with no external provider version applicable.

Target and artifact context

A disposable universal Mach-O copied from the platform's true executable. File inspection reported x86_64, arm64e and arm64e.x1 slices. The fixture client only answers health; no native procedure analysis is performed.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

All three controlled replays produced command exit status 1 and this error:

{
  "error": "Analysis failed",
  "code": "invalid_request",
  "category": "invalid_input",
  "message": "Analysis input is invalid. Check the arguments and try again.",
  "retryable": false,
  "remediation": {
    "action": "Read the declared input schema or CLI usage for trace_native_values; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call."
  },
  "recovery": {
    "kind": "inspect_input_contract",
    "operation": "trace_native_values"
  }
}

The same replay's independent canonical validation and safe issue projection returned:

[{"path":["procedure"],"reason":"out_of_range","minimum":1}]

Its client event sequence was ["fixture-client-created","health","close"]; any other provider operation would fail an assertion. The built dispatcher's schema fragment was:

{"procedure":{"type":"string","description":"Explicit native procedure symbol or address"}}

procedure is in args.required, with no minLength.

Contract and reachability references at the baseline:

  • docs/tool-design.md:339-351: CLI canonical-field parity, empty-string checks, and expected-input field/constraint retention; a schema cause alone does not supply correction metadata.
  • AGENTS.md, Architecture and Cleanup: meaningful failure reasons must survive boundaries.
  • docs/mcp-contracts.md:144-147: input-contract recovery asks callers to correct listed fields. This explains the remediation text; the CLI-specific requirement above supplies the direct contract.
  • src/cli/coreNativeCommands.ts:149-155,208-225: the CLI positional is an unconstrained string and is forwarded to the shared workflow.
  • src/domain/native/nativeValueTrace.ts:5-6 and src/contracts/enhancedInputs.ts:22: the canonical procedure is nonempty.
  • src/application/DirectAnalysis.ts:454-465,602-608: target open precedes composed workflow validation. src/application/binary/BinarySession.ts:641-649 creates a client and requests health during open.
  • src/application/EnhancedTools.ts:125-129 and src/application/EnhancedToolTypes.ts:28-32: failed schema parsing becomes an input error with only a cause.
  • src/domain/analysisErrorCore.ts:108-126 and src/domain/analysisErrorProjection.ts:363-379: the default empty issue list prevents caller issue details from being projected.

Typed outcome or recovery expected

Keep invalid_request, invalid_input, failure status and inspect_input_contract recovery. Preserve an issue such as {"path":["procedure"],"reason":"out_of_range","minimum":1}, or reject the empty positional at CLI admission with its equivalent field-specific diagnostic.

Analysis or suspected cause

Source-derived: the CLI accepts an empty positional, while the shared workflow schema rejects it. The enhanced-input error constructor retains only the Zod cause, leaving the separate safe issue list empty. The projector consequently has no caller correction metadata to emit. This accounts for the controlled replay without assuming any particular native provider failure.

The documented relaxation of MCP advertisements in docs/mcp-contracts.md:1280-1308 applies to MCP client compatibility. It does not document this missing constraint in the CLI positional schema or waive the expected-input diagnostic policy.

Additional context

Open #7549 has the same broad omission pattern for ADB local-file admission. Its closure requires missing/non-regular-file attribution in a different producer; that would not repair enhanced procedure validation.

Closed #5433 concerns replacing a failed seed provider result. The current workflow preserves those seed errors. This request fails shared schema validation before seed analysis, so it is a different closure signal, with no established regression of that fix. Closed #5971 fixes analogous Android schema diagnostics in its own service. Merged PR #1175 corrects defaulted fields advertised as required; it does not address the procedure minimum.

Duplicate searches covered open and closed issues, open/closed/merged PRs and issue comments using command names, empty procedure, minLength, enhanced input issues and generic correction wording. No same-contract report was found. No last-known-good revision or introducing commit was established.

Triage assessment

  • Impact level: P3
  • Assessment status: supported
  • Impact basis: Incorrect correction/discovery metadata accompanies a correctly refused malformed request; no consequential change to valid results or demonstrated failed recovery was observed.
  • Workaround status: available
  • Workaround basis: Supplying a nonempty procedure avoids this exact schema violation; real-provider resolution of any particular selector was not tested.
Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7572 ### 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 CLI ### Observed behavior For the registered CLI request `trace-native-values <Mach-O> '' --json`, a controlled fixture session reached the request's shared validation and returned `invalid_request` / `invalid_input` with no `details` member. The message only says to check the arguments, and recovery says to correct “the listed fields,” without naming a field. The canonical validator independently identified `procedure` with minimum length 1. The built command's `--schema --json` output advertises `procedure` as a required string without `minLength`, so that discovery surface does not reveal the rejected constraint either. The controlled replay used the production command registration, target inspection, one-shot analysis, session, validation and projection, with only the session factory replaced by the repository's fixture provider. It ran no external engine. This is not a live Hopper/Ghidra dispatcher reproduction. The fixture client was created and received `health` before the refusal; validation therefore does not precede provider startup. ### Reproduction 1. Use the baseline's locked dependencies and built runtime with Node.js v24.22.1. Start at the repository root with that Node first on PATH. Create a copied Mach-O and inspect the real command schema: ```sh WORK=$(mktemp -d) mkdir -p "$WORK/home" "$WORK/config" "$WORK/cache" "$WORK/data" "$WORK/state" "$WORK/tmp" cp /usr/bin/true "$WORK/target" node scripts/rea.mjs trace-native-values --schema --json ``` 2. Run this inline controlled replay. It registers the real CLI command and injects an execution-free fixture session. Assertions check the refusal, the missing diagnostic, and the independently available field constraint. The probe captures the command's exit status and resets its own status after verifying it. ```sh env -i PATH="$PATH" HOME="$WORK/home" \ XDG_CONFIG_HOME="$WORK/config" XDG_CACHE_HOME="$WORK/cache" \ XDG_DATA_HOME="$WORK/data" XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/tmp" \ TRACE_FIXTURE="$WORK/target" node --import tsx --input-type=module <<'JS' import assert from 'node:assert/strict'; import { pathToFileURL } from 'node:url'; import { resolve } from 'node:path'; const load = p => import(pathToFileURL(resolve(p)).href); const { Cli } = await load('node_modules/incur/dist/index.js'); const { registerCoreNativeCommands } = await load('src/cli/coreNativeCommands.ts'); const { runDirectAnalysis } = await load('src/application/DirectAnalysis.ts'); const { parseConfig } = await load('src/config/parseConfig.ts'); const { silentLogger } = await load('src/logger.ts'); const { createTestBinarySession } = await load('tests/fixtures/binarySession.ts'); const { observed } = await load('tests/fixtures/analysisExecution.ts'); const { ok } = await load('src/domain/result.ts'); const { nativeValueTraceInputSchema } = await load('src/domain/native/nativeValueTrace.ts'); const { projectInputIssues } = await load('src/domain/inputIssueProjection.ts'); const events = []; const session = createTestBinarySession(() => { events.push('fixture-client-created'); return { execute: async operation => { events.push(operation); assert.equal(operation, 'health'); return observed(null); }, close: async () => { events.push('close'); return ok(null); }, }; }); const forbidden = () => { throw new Error('Unexpected session factory'); }; const dependencies = { readConfiguration: () => parseConfig({}), createBinarySession: () => session, createNativeDispatchBinarySession: forbidden, createArtifactBinarySession: forbidden, createNativeBinarySession: forbidden, createManagedBinarySession: forbidden, }; const cli = Cli.create({ name: 'rea' }); registerCoreNativeCommands(cli, silentLogger, (...args) => runDirectAnalysis(dependencies, ...args)); let stdout = ''; await cli.serve(['trace-native-values', process.env.TRACE_FIXTURE, '', '--json'], { env: {}, stdout: text => { stdout += text; }, exit: () => {}, }); const error = JSON.parse(stdout), commandExit = process.exitCode; process.exitCode = 0; assert.equal(commandExit, 1); assert.equal(error.code, 'invalid_request'); assert.equal(error.details, undefined); assert.deepEqual(events, ['fixture-client-created', 'health', 'close']); const parsed = nativeValueTraceInputSchema.safeParse({procedure: ''}); assert.equal(parsed.success, false); const issues = projectInputIssues(parsed.error.issues, {procedure: ''}); assert.deepEqual(issues, [{path: ['procedure'], reason: 'out_of_range', minimum: 1}]); console.log(JSON.stringify({commandExit, error, events, issues})); JS ``` The inline replay was executed three times, including one execution of the complete reproduction block; all three runs passed every assertion and produced the same missing-field diagnostic. No checkout changes or graphical provider are required. Do not replace the fixture replay with a default-provider invocation on a valid target when avoiding provider startup is required. ### Expected behavior An expected input rejection should retain the caller field and safe constraint, so the caller can identify the correction: `procedure` must have at least one character. The CLI's discovery schema should express that same nonempty semantic field. The current rejection code and failure exit status can remain. The project's input-error policy explicitly requires caller field/index paths and safe constraints; its CLI translation policy requires canonical semantic fields and schema/parser checks for empty strings. The exact contract references are listed under Evidence. ### Impact and scope P3: malformed input remains refused, but its correction diagnostic omits the rejected field and bound, and CLI schema discovery omits that bound too. A caller can supply a nonempty procedure instead. No valid-analysis failure, incorrect graph, data loss, or actual failed agent recovery was demonstrated. Observed runtime scope is the controlled CLI fixture replay, three times. Live provider behavior, MCP admission, other commands and other platforms were not exercised. The separate real dispatcher control with an absent target and the same empty procedure returned `target_unavailable`; target access can mask this later input failure. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` Source commit: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js v24.22.1; macOS 27.0.1, build 26A434; arm64. Built CLI runtime and locked dependencies at the stated baseline. The supplemental replay uses the repository's fixture provider, with no external provider version applicable. ### Target and artifact context A disposable universal Mach-O copied from the platform's `true` executable. File inspection reported x86_64, arm64e and arm64e.x1 slices. The fixture client only answers health; no native procedure analysis is performed. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence All three controlled replays produced command exit status 1 and this error: ```json { "error": "Analysis failed", "code": "invalid_request", "category": "invalid_input", "message": "Analysis input is invalid. Check the arguments and try again.", "retryable": false, "remediation": { "action": "Read the declared input schema or CLI usage for trace_native_values; if using MCP and the catalog may be stale, call tools/list. Correct the listed fields, then retry. Do not repeat the unchanged call." }, "recovery": { "kind": "inspect_input_contract", "operation": "trace_native_values" } } ``` The same replay's independent canonical validation and safe issue projection returned: ```json [{"path":["procedure"],"reason":"out_of_range","minimum":1}] ``` Its client event sequence was `["fixture-client-created","health","close"]`; any other provider operation would fail an assertion. The built dispatcher's schema fragment was: ```json {"procedure":{"type":"string","description":"Explicit native procedure symbol or address"}} ``` `procedure` is in `args.required`, with no `minLength`. Contract and reachability references at the baseline: - `docs/tool-design.md:339-351`: CLI canonical-field parity, empty-string checks, and expected-input field/constraint retention; a schema cause alone does not supply correction metadata. - `AGENTS.md`, Architecture and Cleanup: meaningful failure reasons must survive boundaries. - `docs/mcp-contracts.md:144-147`: input-contract recovery asks callers to correct listed fields. This explains the remediation text; the CLI-specific requirement above supplies the direct contract. - `src/cli/coreNativeCommands.ts:149-155,208-225`: the CLI positional is an unconstrained string and is forwarded to the shared workflow. - `src/domain/native/nativeValueTrace.ts:5-6` and `src/contracts/enhancedInputs.ts:22`: the canonical procedure is nonempty. - `src/application/DirectAnalysis.ts:454-465,602-608`: target open precedes composed workflow validation. `src/application/binary/BinarySession.ts:641-649` creates a client and requests health during open. - `src/application/EnhancedTools.ts:125-129` and `src/application/EnhancedToolTypes.ts:28-32`: failed schema parsing becomes an input error with only a cause. - `src/domain/analysisErrorCore.ts:108-126` and `src/domain/analysisErrorProjection.ts:363-379`: the default empty issue list prevents caller issue details from being projected. ### Typed outcome or recovery expected Keep `invalid_request`, `invalid_input`, failure status and `inspect_input_contract` recovery. Preserve an issue such as `{"path":["procedure"],"reason":"out_of_range","minimum":1}`, or reject the empty positional at CLI admission with its equivalent field-specific diagnostic. ### Analysis or suspected cause Source-derived: the CLI accepts an empty positional, while the shared workflow schema rejects it. The enhanced-input error constructor retains only the Zod cause, leaving the separate safe issue list empty. The projector consequently has no caller correction metadata to emit. This accounts for the controlled replay without assuming any particular native provider failure. The documented relaxation of MCP advertisements in `docs/mcp-contracts.md:1280-1308` applies to MCP client compatibility. It does not document this missing constraint in the CLI positional schema or waive the expected-input diagnostic policy. ### Additional context Open [#7549](https://github.com/morluto/rea/issues/7549) has the same broad omission pattern for ADB local-file admission. Its closure requires missing/non-regular-file attribution in a different producer; that would not repair enhanced procedure validation. Closed [#5433](https://github.com/morluto/rea/issues/5433) concerns replacing a failed seed provider result. The current workflow preserves those seed errors. This request fails shared schema validation before seed analysis, so it is a different closure signal, with no established regression of that fix. Closed [#5971](https://github.com/morluto/rea/issues/5971) fixes analogous Android schema diagnostics in its own service. Merged [PR #1175](https://github.com/morluto/rea/pull/1175) corrects defaulted fields advertised as required; it does not address the procedure minimum. Duplicate searches covered open and closed issues, open/closed/merged PRs and issue comments using command names, empty procedure, minLength, enhanced input issues and generic correction wording. No same-contract report was found. No last-known-good revision or introducing commit was established. ## Triage assessment - Impact level: P3 - Assessment status: supported - Impact basis: Incorrect correction/discovery metadata accompanies a correctly refused malformed request; no consequential change to valid results or demonstrated failed recovery was observed. - Workaround status: available - Workaround basis: Supplying a nonempty procedure avoids this exact schema violation; real-provider resolution of any particular selector was not tested.
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#4510
No description provided.