[GH-ISSUE #5947] [Bug] Missing CDP target detail suppresses refresh-discovery recovery #3617

Closed
opened 2026-10-31 00:42:25 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

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

Area

Process capture or runtime observation

Observed behavior

inspect-web-page with an ID absent from a valid endpoint inventory correctly fails, but both message and remediation.action only say Target stale-page was not found at the selected endpoint. The reason-specific instruction to refresh discovery and select a returned ID is lost. This was reproduced through the built CLI against a simulated discovery endpoint.

Reproduction

From an already-built source checkout with the repository-pinned Node.js on PATH, execute the following Python procedure. It starts an HTTP-only loopback discovery fixture, lists its empty inventory, and inspects an absent ID. No real browser or restart is involved.

import http.server
import json
import os
import pathlib
import subprocess
import tempfile
import threading

requests = []
class Discovery(http.server.BaseHTTPRequestHandler):
    def log_message(self, *args):
        pass
    def do_GET(self):
        requests.append(self.path)
        value = [] if self.path == '/json/list' else {
            'Browser': 'Fixture/1', 'Protocol-Version': '1.3',
            'User-Agent': 'fixture', 'V8-Version': '1', 'WebKit-Version': '1',
            'webSocketDebuggerUrl':
                f'ws://127.0.0.1:{self.server.server_port}/devtools/browser/fixture'
        }
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(json.dumps(value).encode())

server = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Discovery)
thread = threading.Thread(target=server.serve_forever)
thread.start()
try:
    with tempfile.TemporaryDirectory() as directory:
        env = {'PATH': os.environ['PATH'], 'REA_LOG_LEVEL': 'silent'}
        for name in ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME',
                     'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']:
            path = pathlib.Path(directory) / name
            path.mkdir()
            env[name] = str(path)
        endpoint = f'http://127.0.0.1:{server.server_port}'
        for args in [['list-browser-targets', endpoint],
                     ['inspect-web-page', endpoint, 'stale-page']]:
            result = subprocess.run(
                ['node', 'scripts/rea.mjs', *args, '--json'], env=env,
                capture_output=True, text=True, timeout=30)
            output = json.loads(result.stdout)
            print(args[0], 'exit', result.returncode)
            print(json.dumps(output if result.returncode else
                             {'targets': output['normalized_result']['targets']}))
        print('HTTP requests:', requests)
finally:
    server.shutdown()
    server.server_close()
    thread.join()

Expected behavior

Retain the missing ID and preserve the existing recovery instruction to refresh target discovery for the selected endpoint, then retry with an ID from that list. The reason-specific message already defines that recovery. The browser guide explains that IDs are browser-instance-specific and callers should list targets first.

Impact and scope

P3: the observed defect is missing diagnostic/recovery wording. Target selection remains correctly unsuccessful, and the specific ID is preserved. Both executions against the HTTP fixture produced this result; the independent portable procedure above also confirmed successful discovery. A caller can follow the documented list-targets workflow manually. No failed agent loop, successful real-browser recovery, or change in target availability was tested.

REA version

rea-agents 6.7.0, source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

Node.js v24.18.0; macOS 27.0.1 arm64; Python 3 HTTP discovery fixture. Existing built runtime with locked dependencies.

Target and artifact context

Simulated, valid loopback CDP discovery endpoint with an empty target inventory and caller-selected ID stale-page. No browser session or artifact is created.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

list-browser-targets exits 0 and returns targets: []. inspect-web-page exits 1 with:

{"code":"target_unavailable","category":"unavailable","message":"Target stale-page was not found at the selected endpoint.","retryable":false,"remediation":{"action":"Target stale-page was not found at the selected endpoint."},"details":{"operation":"inspect_web_page","reason":"target_not_found"}}

The fixture receives only /json/version and /json/list, once for listing and once for inspection. There is no debugging-session connection. This executes the production discovery, target authorization, and CLI projection paths.

Typed outcome or recovery expected

Keep target_unavailable, details.reason: target_not_found, and the refusal. Make remediation instruct the caller to run list-browser-targets again for the endpoint and choose a returned ID. The public code and retryability policy can remain unchanged.

Analysis or suspected cause

Source-derived: authorizeCdpTarget supplies a fact-only detail containing the ID. BrowserObservationError uses that detail in place of the full reason-specific message, bypassing its refresh instruction. analysisErrorRemediationAction falls back to the resulting user message, so the omission also affects remediation. The specific detail and the existing actionable reason need to survive together.

Additional context

Fetched main ce1f4b1bbaced57529d609f3007a76de583e11c4 leaves these relevant lines unchanged. The error-projection file has only an unrelated captured-output inclusion change; no newer runtime was built or tested. The Inspector refusal report covers attachment/runtime-role rejection under target_not_allowed; its missing-target control already retains discovery recovery. Navigation-refusal presentation and discarded-renderer responsiveness have different triggers and fixes. MCP, Electron, other detail-bearing failures, and a real browser restart were not exercised. Last known good version: NOT_ENOUGH_INFO; none established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5947 ### 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 Process capture or runtime observation ### Observed behavior `inspect-web-page` with an ID absent from a valid endpoint inventory correctly fails, but both `message` and `remediation.action` only say `Target stale-page was not found at the selected endpoint.` The reason-specific instruction to refresh discovery and select a returned ID is lost. This was reproduced through the built CLI against a simulated discovery endpoint. ### Reproduction From an already-built source checkout with the repository-pinned Node.js on PATH, execute the following Python procedure. It starts an HTTP-only loopback discovery fixture, lists its empty inventory, and inspects an absent ID. No real browser or restart is involved. ```python import http.server import json import os import pathlib import subprocess import tempfile import threading requests = [] class Discovery(http.server.BaseHTTPRequestHandler): def log_message(self, *args): pass def do_GET(self): requests.append(self.path) value = [] if self.path == '/json/list' else { 'Browser': 'Fixture/1', 'Protocol-Version': '1.3', 'User-Agent': 'fixture', 'V8-Version': '1', 'WebKit-Version': '1', 'webSocketDebuggerUrl': f'ws://127.0.0.1:{self.server.server_port}/devtools/browser/fixture' } self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(value).encode()) server = http.server.ThreadingHTTPServer(('127.0.0.1', 0), Discovery) thread = threading.Thread(target=server.serve_forever) thread.start() try: with tempfile.TemporaryDirectory() as directory: env = {'PATH': os.environ['PATH'], 'REA_LOG_LEVEL': 'silent'} for name in ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']: path = pathlib.Path(directory) / name path.mkdir() env[name] = str(path) endpoint = f'http://127.0.0.1:{server.server_port}' for args in [['list-browser-targets', endpoint], ['inspect-web-page', endpoint, 'stale-page']]: result = subprocess.run( ['node', 'scripts/rea.mjs', *args, '--json'], env=env, capture_output=True, text=True, timeout=30) output = json.loads(result.stdout) print(args[0], 'exit', result.returncode) print(json.dumps(output if result.returncode else {'targets': output['normalized_result']['targets']})) print('HTTP requests:', requests) finally: server.shutdown() server.server_close() thread.join() ``` ### Expected behavior Retain the missing ID and preserve the existing recovery instruction to refresh target discovery for the selected endpoint, then retry with an ID from that list. The [reason-specific message](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/browserObservationError.ts#L50-L55) already defines that recovery. The [browser guide](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/browser-observation.md#L303-L306) explains that IDs are browser-instance-specific and callers should list targets first. ### Impact and scope P3: the observed defect is missing diagnostic/recovery wording. Target selection remains correctly unsuccessful, and the specific ID is preserved. Both executions against the HTTP fixture produced this result; the independent portable procedure above also confirmed successful discovery. A caller can follow the documented list-targets workflow manually. No failed agent loop, successful real-browser recovery, or change in target availability was tested. ### REA version `rea-agents` 6.7.0, source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform Node.js v24.18.0; macOS 27.0.1 arm64; Python 3 HTTP discovery fixture. Existing built runtime with locked dependencies. ### Target and artifact context Simulated, valid loopback CDP discovery endpoint with an empty target inventory and caller-selected ID `stale-page`. No browser session or artifact is created. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence `list-browser-targets` exits 0 and returns `targets: []`. `inspect-web-page` exits 1 with: ```json {"code":"target_unavailable","category":"unavailable","message":"Target stale-page was not found at the selected endpoint.","retryable":false,"remediation":{"action":"Target stale-page was not found at the selected endpoint."},"details":{"operation":"inspect_web_page","reason":"target_not_found"}} ``` The fixture receives only `/json/version` and `/json/list`, once for listing and once for inspection. There is no debugging-session connection. This executes the production discovery, target authorization, and CLI projection paths. ### Typed outcome or recovery expected Keep `target_unavailable`, `details.reason: target_not_found`, and the refusal. Make remediation instruct the caller to run `list-browser-targets` again for the endpoint and choose a returned ID. The public code and retryability policy can remain unchanged. ### Analysis or suspected cause Source-derived: [authorizeCdpTarget](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/browser/CdpAuthorizedTarget.ts#L18-L24) supplies a fact-only `detail` containing the ID. [BrowserObservationError](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/browserObservationError.ts#L19-L25) uses that detail in place of the full reason-specific message, bypassing its refresh instruction. [analysisErrorRemediationAction](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorPresentation.ts#L131-L142) falls back to the resulting user message, so the omission also affects remediation. The specific detail and the existing actionable reason need to survive together. ### Additional context Fetched main `ce1f4b1bbaced57529d609f3007a76de583e11c4` leaves these relevant lines unchanged. The error-projection file has only an unrelated captured-output inclusion change; no newer runtime was built or tested. The Inspector refusal report covers attachment/runtime-role rejection under `target_not_allowed`; its missing-target control already retains discovery recovery. Navigation-refusal presentation and discarded-renderer responsiveness have different triggers and fixes. MCP, Electron, other detail-bearing failures, and a real browser restart were not exercised. Last known good version: NOT_ENOUGH_INFO; none established.
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#3617
No description provided.