[GH-ISSUE #7568] [Bug] IDA connection errors discard the configured service URL #4509

Closed
opened 2026-10-31 00:50:22 +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/7568

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

I selected a valid headless IDA registration with a non-default loopback MCP URL, then ran:

node scripts/rea.mjs analyze /bin/ls --provider ida --format json

The local fixture received POST /mcp and answered HTTP 404 with fixture-ida-not-found. REA exited 1 with execution_failure, but its complete stdout/stderr did not contain the selected URL. The returned reason was Error POSTing to endpoint: \nfixture-ida-not-found, with generic registration/target recovery guidance. Repeating with another listener port produced the same URL omission. After stopping the first listener, the same URL produced fetch failed, again without the URL.

The returned failure cannot identify the configured service that was attempted unless the caller saves that identity separately.

Reproduction

  1. From the built baseline checkout, put Node.js 24.22.1 first on PATH. Run this inline fixture from the checkout root. It creates isolated HOME/XDG state, starts only its own loopback listeners, and terminates/reaps each by its recorded PID. No IDA installation is needed.
python3 - <<'PYTHON'
import json, os, subprocess, tempfile
from pathlib import Path

fixture = """
const http = require('node:http');
http.createServer((req, res) => {
  console.error(req.method + ' ' + req.url);
  res.writeHead(404, {'content-type': 'text/plain'});
  res.end('fixture-ida-not-found');
}).listen(0, '127.0.0.1', function () { console.log(this.address().port); });
"""
with tempfile.TemporaryDirectory() as directory:
    work = Path(directory)
    env = {'PATH': os.environ['PATH']}
    for key in ('HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME',
                'XDG_DATA_HOME', 'XDG_STATE_HOME', 'TMPDIR'):
        path = work / key
        path.mkdir()
        env[key] = str(path)
    registration = work / 'ida.json'
    def analyze(endpoint):
        registration.write_text(json.dumps({'mode': 'headless', 'url': endpoint}))
        result = subprocess.run(
            ['node', 'scripts/rea.mjs', 'analyze', '/bin/ls',
             '--provider', 'ida', '--format', 'json'],
            env={**env, 'REA_IDA_MCP_CONFIG': str(registration)},
            capture_output=True, text=True, timeout=45)
        print(json.dumps({'selected_endpoint': endpoint, 'exit': result.returncode,
                          'endpoint_retained': endpoint in result.stdout + result.stderr,
                          'error': json.loads(result.stdout), 'stderr': result.stderr}))
    for iteration in range(2):
        server = subprocess.Popen(['node', '-e', fixture], env=env,
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        try:
            endpoint = f'http://127.0.0.1:{int(server.stdout.readline())}/mcp'
            print('listener:', subprocess.check_output(
                ['ps', '-p', str(server.pid), '-o', 'pid=,stat=,comm='], text=True).strip())
            analyze(endpoint)
        finally:
            server.terminate()
            server.wait(timeout=5)
            print('requests:', server.stderr.read().strip())
            after = subprocess.run(['ps', '-p', str(server.pid), '-o', 'pid='],
                                   capture_output=True, text=True)
            print('listener remains:', bool(after.stdout.strip()))
        if iteration == 0:
            analyze(endpoint)  # same endpoint after its listener has stopped
PYTHON
  1. Read the three JSON rows: two live HTTP 404 cases and one stopped-listener control. Every row has exit 1 and endpoint_retained: false. Each live fixture logs exactly one POST /mcp; both process checks after cleanup print listener remains: False. The control uses the first listener's actual URL after its process has stopped.

This reduced reproduction ran once, covering two distinct live endpoints and one stopped-endpoint control. A separate initial driver independently reproduced two additional HTTP 404 cases and one stopped-listener control with the same CLI output. All six analysis calls omitted the selected URL.

Expected behavior

Retain the caller-selected, credential-free IDA service URL in the returned connection-failure diagnostics, so the failure remains attributable to the service REA attempted. No particular field name or replacement error code is required.

The repository's caller-input preservation invariant explicitly includes URLs, and the IDA guide supports this local Streamable HTTP registration form. The guide's separate promise to retain a registration filename on configuration read/validation failures is narrower; this report does not extend that promise to every runtime error.

Impact and scope

P2: a saved failure loses the selected service identity on the demonstrated supported IDA connection path. This matters when diagnosing or retaining errors from different configured local services. The fixture demonstrates a practical workaround: record the selected URL alongside the CLI result before invoking REA.

Observed scope is the public CLI on this host, before a successful MCP initialization or IDA worker acquisition. The response body remains available, and the command correctly fails. No wrong analysis result, target mutation, data loss, or security boundary crossing was observed. Real IDA behavior, successful connections, direct MCP projection, other platforms, authentication, DNS, TLS, timeouts, and later-session failures were not exercised.

REA version

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

6.8.0

Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; arm64. The upstream fixture uses Node's built-in HTTP server. No IDA engine or GUI was launched.

Target and artifact context

/bin/ls is the existing local Mach-O input used to reach the selected provider. The failure occurs while establishing the upstream connection for list_documents, before target-specific IDA analysis.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The reduced fixture selected http://127.0.0.1:54011/mcp for its first live case; the port changes on subsequent runs. Its listener logged POST /mcp. Complete CLI stdout, exit 1 and empty stderr:

{"error":"Analysis failed","code":"execution_failure","category":"execution_failure","message":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.","retryable":false,"remediation":{"action":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed."},"details":{"provider_id":"ida","operation":"list_documents","diagnostics":{"reason":"Error POSTing to endpoint: \nfixture-ida-not-found","recovery":"Check the configured upstream MCP registration and selected target; see docs/ida-provider.md."}}}

The second live case selected port 54019 and produced the same error. The stopped-listener control kept port 54011 and returned the same fields with details.diagnostics.reason: "fetch failed". None contained its selected URL.

I also ran node scripts/rea.mjs doctor --provider ida --json with the initial driver's valid HTTP registration. It exited 0 and explicitly said upstream availability is checked when analysis starts. Its JSON retains the registration filename in registrationEnvironment; this is expected registration validation behavior, not evidence that the endpoint is live.

Contract and source locations at the baseline:

  • AGENTS.md:13 requires preserving caller-selected inputs, including URLs.
  • docs/ida-provider.md:150-162 supports headless local Streamable HTTP registrations. Lines 44-46 discuss the separate invalid/unreadable-registration diagnostic contract.
  • src/ida/IdaMcpConnection.ts:410-477,501-526 selects the URL, establishes the connection, and translates its failure without attaching that URL. The no-stderr path throws a plain error with sanitized message text.
  • src/ida/IdaSessionClient.ts:512-521 maps that error to adapter diagnostics containing reason and recovery, without the selected URL.
  • src/domain/analysisErrorPresentation.ts:435-436 supplies the generic adapter-failure message.

Typed outcome or recovery expected

Return the typed failure with the selected service URL available in its diagnostics. Preserve the failure reason and existing credential-redaction rules. For the live fixture, the returned error should identify the non-default loopback URL that received the request; for the stopped-listener control, it should identify the same attempted URL.

Analysis or suspected cause

Source-derived: the IDA connection owns the selected URL and passes it to the SDK, but its error translation does not attach endpoint identity. The session adapter then keeps only the resulting message as its reason. The domain projection cannot recover the omitted URL from those supplied facts. This accounts for the observed CLI output; no fix was implemented during verification.

Additional context

The duplicate search covered open/closed issues, issue comments, and open/closed/merged PRs for IDA endpoint, transport, diagnostics, connection, registration_path, IdaSessionClient, HTTP status, and the SDK error wording.

Related #7557 reports the JEB analogue. Its direct-error mapping is in src/jeb/JebProvider.ts; the IDA mapping above is separate. Restoring the URL only for direct JEB inspection would leave this IDA analysis reproduction failing, and restoring it only for IDA would leave that JEB reproduction failing. They have independent adapter closure checks despite the shared preservation invariant and SDK.

Closed #5451 concerns JEB discovery recovery advice, including its reachable-HTTP comment, and #3908 concerns a selected artifact path on a JEB engine refusal. IDA #7533, closed #5828, and closed #5771 concern invalid/unreadable registration diagnostics. Their documented closures do not address this valid registration's failed connection.

The broader IDA producer-diagnostic work in PR #2695 retains sanitized reasons and known HTTP status internally; it does not supply the selected URL to the caller-facing error. PR #2449 explicitly preserves the existing HTTP/no-stderr path while adding stdio stderr retention. No last-known-good endpoint-retention behavior or regression of those fixes was established.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: All six independently executed IDA analysis failures discarded the selected service URL, so the returned error alone cannot identify the attempted endpoint. The failures remain typed and preserve their response reason; no target result was lost.
  • Workaround status: available
  • Workaround basis: The executed inline driver records the selected URL alongside each error, preserving attribution without changing REA or weakening a boundary.
Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7568 ### 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 I selected a valid headless IDA registration with a non-default loopback MCP URL, then ran: ```sh node scripts/rea.mjs analyze /bin/ls --provider ida --format json ``` The local fixture received `POST /mcp` and answered HTTP 404 with `fixture-ida-not-found`. REA exited 1 with `execution_failure`, but its complete stdout/stderr did not contain the selected URL. The returned reason was `Error POSTing to endpoint: \nfixture-ida-not-found`, with generic registration/target recovery guidance. Repeating with another listener port produced the same URL omission. After stopping the first listener, the same URL produced `fetch failed`, again without the URL. The returned failure cannot identify the configured service that was attempted unless the caller saves that identity separately. ### Reproduction 1. From the built baseline checkout, put Node.js 24.22.1 first on PATH. Run this inline fixture from the checkout root. It creates isolated HOME/XDG state, starts only its own loopback listeners, and terminates/reaps each by its recorded PID. No IDA installation is needed. ```sh python3 - <<'PYTHON' import json, os, subprocess, tempfile from pathlib import Path fixture = """ const http = require('node:http'); http.createServer((req, res) => { console.error(req.method + ' ' + req.url); res.writeHead(404, {'content-type': 'text/plain'}); res.end('fixture-ida-not-found'); }).listen(0, '127.0.0.1', function () { console.log(this.address().port); }); """ with tempfile.TemporaryDirectory() as directory: work = Path(directory) env = {'PATH': os.environ['PATH']} for key in ('HOME', 'XDG_CONFIG_HOME', 'XDG_CACHE_HOME', 'XDG_DATA_HOME', 'XDG_STATE_HOME', 'TMPDIR'): path = work / key path.mkdir() env[key] = str(path) registration = work / 'ida.json' def analyze(endpoint): registration.write_text(json.dumps({'mode': 'headless', 'url': endpoint})) result = subprocess.run( ['node', 'scripts/rea.mjs', 'analyze', '/bin/ls', '--provider', 'ida', '--format', 'json'], env={**env, 'REA_IDA_MCP_CONFIG': str(registration)}, capture_output=True, text=True, timeout=45) print(json.dumps({'selected_endpoint': endpoint, 'exit': result.returncode, 'endpoint_retained': endpoint in result.stdout + result.stderr, 'error': json.loads(result.stdout), 'stderr': result.stderr})) for iteration in range(2): server = subprocess.Popen(['node', '-e', fixture], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) try: endpoint = f'http://127.0.0.1:{int(server.stdout.readline())}/mcp' print('listener:', subprocess.check_output( ['ps', '-p', str(server.pid), '-o', 'pid=,stat=,comm='], text=True).strip()) analyze(endpoint) finally: server.terminate() server.wait(timeout=5) print('requests:', server.stderr.read().strip()) after = subprocess.run(['ps', '-p', str(server.pid), '-o', 'pid='], capture_output=True, text=True) print('listener remains:', bool(after.stdout.strip())) if iteration == 0: analyze(endpoint) # same endpoint after its listener has stopped PYTHON ``` 2. Read the three JSON rows: two live HTTP 404 cases and one stopped-listener control. Every row has exit 1 and `endpoint_retained: false`. Each live fixture logs exactly one `POST /mcp`; both process checks after cleanup print `listener remains: False`. The control uses the first listener's actual URL after its process has stopped. This reduced reproduction ran once, covering two distinct live endpoints and one stopped-endpoint control. A separate initial driver independently reproduced two additional HTTP 404 cases and one stopped-listener control with the same CLI output. All six analysis calls omitted the selected URL. ### Expected behavior Retain the caller-selected, credential-free IDA service URL in the returned connection-failure diagnostics, so the failure remains attributable to the service REA attempted. No particular field name or replacement error code is required. The repository's caller-input preservation invariant explicitly includes URLs, and the IDA guide supports this local Streamable HTTP registration form. The guide's separate promise to retain a registration filename on configuration read/validation failures is narrower; this report does not extend that promise to every runtime error. ### Impact and scope P2: a saved failure loses the selected service identity on the demonstrated supported IDA connection path. This matters when diagnosing or retaining errors from different configured local services. The fixture demonstrates a practical workaround: record the selected URL alongside the CLI result before invoking REA. Observed scope is the public CLI on this host, before a successful MCP initialization or IDA worker acquisition. The response body remains available, and the command correctly fails. No wrong analysis result, target mutation, data loss, or security boundary crossing was observed. Real IDA behavior, successful connections, direct MCP projection, other platforms, authentication, DNS, TLS, timeouts, and later-session failures were not exercised. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js `v24.22.1`; macOS `27.0.1`; `arm64`. The upstream fixture uses Node's built-in HTTP server. No IDA engine or GUI was launched. ### Target and artifact context `/bin/ls` is the existing local Mach-O input used to reach the selected provider. The failure occurs while establishing the upstream connection for `list_documents`, before target-specific IDA analysis. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The reduced fixture selected `http://127.0.0.1:54011/mcp` for its first live case; the port changes on subsequent runs. Its listener logged `POST /mcp`. Complete CLI stdout, exit 1 and empty stderr: ```json {"error":"Analysis failed","code":"execution_failure","category":"execution_failure","message":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.","retryable":false,"remediation":{"action":"Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed."},"details":{"provider_id":"ida","operation":"list_documents","diagnostics":{"reason":"Error POSTing to endpoint: \nfixture-ida-not-found","recovery":"Check the configured upstream MCP registration and selected target; see docs/ida-provider.md."}}} ``` The second live case selected port 54019 and produced the same error. The stopped-listener control kept port 54011 and returned the same fields with `details.diagnostics.reason: "fetch failed"`. None contained its selected URL. I also ran `node scripts/rea.mjs doctor --provider ida --json` with the initial driver's valid HTTP registration. It exited 0 and explicitly said upstream availability is checked when analysis starts. Its JSON retains the registration filename in `registrationEnvironment`; this is expected registration validation behavior, not evidence that the endpoint is live. Contract and source locations at the baseline: - `AGENTS.md:13` requires preserving caller-selected inputs, including URLs. - `docs/ida-provider.md:150-162` supports headless local Streamable HTTP registrations. Lines 44-46 discuss the separate invalid/unreadable-registration diagnostic contract. - `src/ida/IdaMcpConnection.ts:410-477,501-526` selects the URL, establishes the connection, and translates its failure without attaching that URL. The no-stderr path throws a plain error with sanitized message text. - `src/ida/IdaSessionClient.ts:512-521` maps that error to adapter diagnostics containing reason and recovery, without the selected URL. - `src/domain/analysisErrorPresentation.ts:435-436` supplies the generic adapter-failure message. ### Typed outcome or recovery expected Return the typed failure with the selected service URL available in its diagnostics. Preserve the failure reason and existing credential-redaction rules. For the live fixture, the returned error should identify the non-default loopback URL that received the request; for the stopped-listener control, it should identify the same attempted URL. ### Analysis or suspected cause Source-derived: the IDA connection owns the selected URL and passes it to the SDK, but its error translation does not attach endpoint identity. The session adapter then keeps only the resulting message as its reason. The domain projection cannot recover the omitted URL from those supplied facts. This accounts for the observed CLI output; no fix was implemented during verification. ### Additional context The duplicate search covered open/closed issues, issue comments, and open/closed/merged PRs for IDA endpoint, transport, diagnostics, connection, `registration_path`, `IdaSessionClient`, HTTP status, and the SDK error wording. Related [#7557](https://github.com/morluto/rea/issues/7557) reports the JEB analogue. Its direct-error mapping is in `src/jeb/JebProvider.ts`; the IDA mapping above is separate. Restoring the URL only for direct JEB inspection would leave this IDA analysis reproduction failing, and restoring it only for IDA would leave that JEB reproduction failing. They have independent adapter closure checks despite the shared preservation invariant and SDK. Closed [#5451](https://github.com/morluto/rea/issues/5451) concerns JEB discovery recovery advice, including its reachable-HTTP comment, and [#3908](https://github.com/morluto/rea/issues/3908) concerns a selected artifact path on a JEB engine refusal. IDA [#7533](https://github.com/morluto/rea/issues/7533), closed [#5828](https://github.com/morluto/rea/issues/5828), and closed [#5771](https://github.com/morluto/rea/issues/5771) concern invalid/unreadable registration diagnostics. Their documented closures do not address this valid registration's failed connection. The broader IDA producer-diagnostic work in [PR #2695](https://github.com/morluto/rea/pull/2695) retains sanitized reasons and known HTTP status internally; it does not supply the selected URL to the caller-facing error. [PR #2449](https://github.com/morluto/rea/pull/2449) explicitly preserves the existing HTTP/no-stderr path while adding stdio stderr retention. No last-known-good endpoint-retention behavior or regression of those fixes was established. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: All six independently executed IDA analysis failures discarded the selected service URL, so the returned error alone cannot identify the attempted endpoint. The failures remain typed and preserve their response reason; no target result was lost. - Workaround status: available - Workaround basis: The executed inline driver records the selected URL alongside each error, preserving attribution without changing REA or weakening a boundary.
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#4509
No description provided.