[GH-ISSUE #7636] [Bug] Overlong historical capture paths receive provider-failure classification #4561

Open
opened 2026-10-31 00:50:47 +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/7636

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

The public CLI misclassifies an initially unresolvable capture selection as a failed analysis provider. Passing an existing parent directory followed by a 300-character ASCII filename to:

node scripts/rea.mjs inspect-web-network-capture "$long_path" har --json

returns exit 1 with code: "execution_failure", category: "execution_failure", and advice to run rea doctor. The result retains ENAMETOOLONG under capture-read diagnostics but provides no capture_path input issue. A host lstat on that selection independently reports ENAMETOOLONG.

A minimal HAR succeeds. A missing capture, directory selection and symlink selection each return invalid_request / invalid_input with a capture_path issue. All commands have empty stderr.

Reproduction

  1. From the assigned baseline's built checkout with Node.js v24.22.1 on PATH, run this self-contained recipe. It creates all fixtures, isolates HOME/XDG/temporary state, and deletes them in finally. It uses the local HAR worker for the valid control and never opens a browser or fetches recorded destinations.
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, writeFileSync, symlinkSync, lstatSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
import { spawnSync } from 'node:child_process';
const work = mkdtempSync(join(tmpdir(), 'capture-path-'));
const cli = resolve('scripts/rea.mjs');
const env = { ...process.env };
for (const key of Object.keys(env)) {
  if (/^(REA_|HOPPER_|GHIDRA_)/.test(key)) delete env[key];
}
try {
  for (const key of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) {
    env[key] = join(work, key.toLowerCase());
    mkdirSync(env[key]);
  }
  writeFileSync(join(work, 'valid.har'), JSON.stringify({log: {version: '1.2', creator: {name: 'fixture', version: '1'}, entries: []}}));
  mkdirSync(join(work, 'directory'));
  symlinkSync('valid.har', join(work, 'link'));
  const longPath = join(work, 'a'.repeat(300));
  assert.throws(() => lstatSync(longPath), {code: 'ENAMETOOLONG'});
  for (const [label, selected, expected] of [
    ['valid', join(work, 'valid.har'), undefined],
    ['missing', join(work, 'missing.har'), 'invalid_request'],
    ['directory', join(work, 'directory'), 'invalid_request'],
    ['symlink', join(work, 'link'), 'invalid_request'],
    ['long-1', longPath, 'execution_failure'],
    ['long-2', longPath, 'execution_failure'],
    ['long-3', longPath, 'execution_failure'],
  ]) {
    const run = spawnSync(process.execPath, [cli, 'inspect-web-network-capture', selected, 'har', '--json'], {env, encoding: 'utf8', timeout: 30000});
    if (run.error) throw run.error;
    const result = JSON.parse(run.stdout);
    assert.equal(run.status, expected ? 1 : 0);
    assert.equal(run.stderr, '');
    assert.equal(result.code, expected);
    if (expected === 'invalid_request') {
      assert.equal(result.category, 'invalid_input');
      assert.deepEqual(result.details.issues[0].path, ['capture_path']);
    }
    if (expected === 'execution_failure') {
      assert.equal(result.category, 'execution_failure');
      assert.equal(result.details.diagnostics.phase, 'capture-read');
      assert.match(result.details.diagnostics.reason, /ENAMETOOLONG/);
      assert.match(result.remediation.action, /rea doctor/);
      assert.equal(result.details.issues, undefined);
    }
    console.log(JSON.stringify({label, exit: run.status, code: result.code ?? 'success', category: result.category, phase: result.details?.diagnostics?.phase, errno: result.details?.diagnostics?.reason?.split(':')[0]}));
  }
} finally {
  rmSync(work, {recursive: true, force: true});
}
JS
  1. Compare the three long-* rows with the controls. The recipe exits 0 after asserting the incorrect current classification, retained errno, doctor advice and absent input issues. This complete recipe ran once; an earlier independent driver reproduced three more overlong failures, for six failures in total.

Expected behavior

An initial selected-capture path-resolution refusal should identify the caller's capture_path as invalid input, preserve the host pathname-limit reason, and direct correction to that selection. Use the capture reader's existing invalid_request / invalid_input outcome rather than assigning a provider execution fault. Genuine permission, resource, decoder, cleanup and post-admission integrity failures should retain their distinct classifications.

This expectation follows the capture reader's own input-failure mapping, its documented distinction between missing-file, permission and acquisition-change failures, and the requirement that expected input failures retain caller field paths. The capture guide does not explicitly enumerate every pathname errno. The precise sources are listed in Evidence below; sibling operation behavior supplies corroboration rather than the contract for this command.

Impact and scope

P2: the machine-readable result assigns a deterministic input-selection failure to the provider execution category and omits field correction metadata. Clients branching on these categories cannot use the normal input-correction result for this case. Actual downstream automated recovery was not tested.

The command safely refuses the selection and retains the native reason, which permits manual diagnosis. A short, readable generated HAR successfully completes the same operation; correcting the selection is a practical recovery. No wrong successful analysis, data loss or security-boundary crossing was observed. The failing name does not identify a host-creatable file that REA ought to decode.

Observed scope is the built HAR CLI on macOS. MCP, mitmproxy, SAZ, qlog, other operating systems, published-package execution and real-world frequency are untested.

REA version

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

6.8.0

The stdout ends with a newline. Executed source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a, using its prebuilt CLI.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; arm64. The valid control uses REA's bundled local HAR decoder. No Hopper or GUI application was launched and no external provider was installed.

Target and artifact context

A self-authored empty HAR 1.2 capture, an absent path, an empty directory, a symlink to the HAR, and an argument whose final component is 300 ASCII characters. No file is created at the overlong selection. All fixture and application-state trees were removed.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

The executed recipe printed:

selection  exit  code               category           phase         errno
valid      0     success
missing    1     invalid_request    invalid_input
directory  1     invalid_request    invalid_input
symlink    1     invalid_request    invalid_input
long-1     1     execution_failure  execution_failure  capture-read  ENAMETOOLONG
long-2     1     execution_failure  execution_failure  capture-read  ENAMETOOLONG
long-3     1     execution_failure  execution_failure  capture-read  ENAMETOOLONG

Each long selection returned this exact message and remediation action:

Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.

The full response included retryable: false, details.provider_id: "har", details.operation: "inspect_web_network_capture", details.diagnostics.phase: "capture-read", the selected path, and an ENAMETOOLONG: name too long, lstat reason. details.issues was absent. The script checks these properties directly.

Sources inspected at the baseline:

  • docs/web-network-captures.md:961-974 distinguishes stable regular-file reads, missing-file input failures, host access denials and acquisition-time integrity failures. docs/tool-design.md:348-351 requires expected input failures to retain caller field paths and correction metadata; AGENTS.md, Architecture and Cleanup, requires meaningful tagged boundary failures.
  • src/browser/history/HistoricalCaptureDecoder.ts:142-168 selects the configured adapter and acquires the capture before setting phase to decoder or creating its private runtime. src/artifacts/readStableArtifact.ts:75 performs the first lstat; no initial file identity has yet been admitted.
  • src/browser/history/CaptureFailures.ts:191-212 handles access-denied errors separately and maps raw ENOENT/ENOTDIR during capture read to AnalysisInputError with a capture_path issue. Its fallback at lines 228-253 constructs ProviderAdapterError for the observed errno.
  • src/artifacts/ArtifactFileOperation.ts:105-129 recognizes ENAMETOOLONG as a caller-selected artifact path-resolution error, distinct from physical I/O and permissions. The stable reader uses this distinction after validation for integrity failures, so the acquisition stage matters.

As an additional executed control, inspect-byte-entropy on the identical 300-character selection returned invalid_request / invalid_input, with a path issue naming ENAMETOOLONG. That control supports the pathname diagnosis but does not define the capture provider's contract.

Typed outcome or recovery expected

invalid_request / invalid_input, with an issue identifying capture_path and retaining ENAMETOOLONG or an equivalent accurate pathname-limit explanation. Ask the caller to select a resolvable capture path rather than run provider diagnostics. Preserve the existing safe refusal and non-retryable status.

Analysis or suspected cause

Source-derived explanation: the initial lstat rejection reaches the capture-specific classifier as a raw host error. Its selected-path branch includes missing/not-a-directory errno values but omits ENAMETOOLONG, causing the provider-adapter fallback to own this input error. The CLI observations establish the wrong classification independently of a proposed implementation. No code change or repaired result was tested.

Additional context

Read-only searches covered open and closed issues, open/closed/merged PRs and issue comments. No matching historical-capture overlong-selection report was found.

Related reports have different restoration conditions:

  • #7607, #7629, and #7573 concern reference-root recovery, process-scenario symlink loops and Evidence write resource failures respectively.
  • Closed #5949 and merged PR #6371 restore plist selected/default errors in a separate owner. Closed #4188, #6740, and #6754 restore SQLite, EVM and entropy classifications. Those fixes do not change this capture reader. The baseline entropy control already returns the expected input result while this capture call still fails.
  • Closed #1128 and #5965 concern decoder heap exhaustion and explicit reply-budget refusal after acquisition. Their resource/output closure guarantees differ from an initially unresolvable selected path.
  • Merged PR #1240 and PR #1396 classify export/trace selections in other readers. Merged PR #1899 adds NUL schema rejection, while PR #946 addresses scenario/JSON-file boundaries. None restores this initial capture-read errno mapping.

This report does not claim regression from any of those fixes. Last known good revision and regression window: NOT_ENOUGH_INFO.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Six real HAR CLI selections received provider execution-failure classification and no capture_path correction issue for a host-confirmed initial pathname-resolution error; valid, missing, directory and symlink controls establish the separate success and input outcomes. This is bounded machine-readable failure misclassification, without observed data loss or a general capture outage.
  • Workaround status: available
  • Workaround basis: The host reason remains readable in diagnostics, and the same command successfully inspects the generated HAR at a short resolvable path. Correct the path selection rather than changing provider setup; the invalid overlong name does not denote a readable capture whose contents must be recovered.
Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7636 ### 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 Evidence, artifacts, or reconstruction ### Observed behavior The public CLI misclassifies an initially unresolvable capture selection as a failed analysis provider. Passing an existing parent directory followed by a 300-character ASCII filename to: ```sh node scripts/rea.mjs inspect-web-network-capture "$long_path" har --json ``` returns exit 1 with `code: "execution_failure"`, `category: "execution_failure"`, and advice to run `rea doctor`. The result retains `ENAMETOOLONG` under capture-read diagnostics but provides no `capture_path` input issue. A host `lstat` on that selection independently reports `ENAMETOOLONG`. A minimal HAR succeeds. A missing capture, directory selection and symlink selection each return `invalid_request` / `invalid_input` with a `capture_path` issue. All commands have empty stderr. ### Reproduction 1. From the assigned baseline's built checkout with Node.js v24.22.1 on PATH, run this self-contained recipe. It creates all fixtures, isolates HOME/XDG/temporary state, and deletes them in `finally`. It uses the local HAR worker for the valid control and never opens a browser or fetches recorded destinations. ```sh node --input-type=module <<'JS' import assert from 'node:assert/strict'; import { mkdtempSync, mkdirSync, writeFileSync, symlinkSync, lstatSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { spawnSync } from 'node:child_process'; const work = mkdtempSync(join(tmpdir(), 'capture-path-')); const cli = resolve('scripts/rea.mjs'); const env = { ...process.env }; for (const key of Object.keys(env)) { if (/^(REA_|HOPPER_|GHIDRA_)/.test(key)) delete env[key]; } try { for (const key of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) { env[key] = join(work, key.toLowerCase()); mkdirSync(env[key]); } writeFileSync(join(work, 'valid.har'), JSON.stringify({log: {version: '1.2', creator: {name: 'fixture', version: '1'}, entries: []}})); mkdirSync(join(work, 'directory')); symlinkSync('valid.har', join(work, 'link')); const longPath = join(work, 'a'.repeat(300)); assert.throws(() => lstatSync(longPath), {code: 'ENAMETOOLONG'}); for (const [label, selected, expected] of [ ['valid', join(work, 'valid.har'), undefined], ['missing', join(work, 'missing.har'), 'invalid_request'], ['directory', join(work, 'directory'), 'invalid_request'], ['symlink', join(work, 'link'), 'invalid_request'], ['long-1', longPath, 'execution_failure'], ['long-2', longPath, 'execution_failure'], ['long-3', longPath, 'execution_failure'], ]) { const run = spawnSync(process.execPath, [cli, 'inspect-web-network-capture', selected, 'har', '--json'], {env, encoding: 'utf8', timeout: 30000}); if (run.error) throw run.error; const result = JSON.parse(run.stdout); assert.equal(run.status, expected ? 1 : 0); assert.equal(run.stderr, ''); assert.equal(result.code, expected); if (expected === 'invalid_request') { assert.equal(result.category, 'invalid_input'); assert.deepEqual(result.details.issues[0].path, ['capture_path']); } if (expected === 'execution_failure') { assert.equal(result.category, 'execution_failure'); assert.equal(result.details.diagnostics.phase, 'capture-read'); assert.match(result.details.diagnostics.reason, /ENAMETOOLONG/); assert.match(result.remediation.action, /rea doctor/); assert.equal(result.details.issues, undefined); } console.log(JSON.stringify({label, exit: run.status, code: result.code ?? 'success', category: result.category, phase: result.details?.diagnostics?.phase, errno: result.details?.diagnostics?.reason?.split(':')[0]})); } } finally { rmSync(work, {recursive: true, force: true}); } JS ``` 2. Compare the three `long-*` rows with the controls. The recipe exits 0 after asserting the incorrect current classification, retained errno, doctor advice and absent input issues. This complete recipe ran once; an earlier independent driver reproduced three more overlong failures, for six failures in total. ### Expected behavior An initial selected-capture path-resolution refusal should identify the caller's `capture_path` as invalid input, preserve the host pathname-limit reason, and direct correction to that selection. Use the capture reader's existing `invalid_request` / `invalid_input` outcome rather than assigning a provider execution fault. Genuine permission, resource, decoder, cleanup and post-admission integrity failures should retain their distinct classifications. This expectation follows the capture reader's own input-failure mapping, its documented distinction between missing-file, permission and acquisition-change failures, and the requirement that expected input failures retain caller field paths. The capture guide does not explicitly enumerate every pathname errno. The precise sources are listed in Evidence below; sibling operation behavior supplies corroboration rather than the contract for this command. ### Impact and scope P2: the machine-readable result assigns a deterministic input-selection failure to the provider execution category and omits field correction metadata. Clients branching on these categories cannot use the normal input-correction result for this case. Actual downstream automated recovery was not tested. The command safely refuses the selection and retains the native reason, which permits manual diagnosis. A short, readable generated HAR successfully completes the same operation; correcting the selection is a practical recovery. No wrong successful analysis, data loss or security-boundary crossing was observed. The failing name does not identify a host-creatable file that REA ought to decode. Observed scope is the built HAR CLI on macOS. MCP, mitmproxy, SAZ, qlog, other operating systems, published-package execution and real-world frequency are untested. ### REA version Exact `node scripts/rea.mjs --version` stdout: ```text 6.8.0 ``` The stdout ends with a newline. Executed source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`, using its prebuilt CLI. ### Runtime and platform Node.js v24.22.1; macOS 27.0.1; arm64. The valid control uses REA's bundled local HAR decoder. No Hopper or GUI application was launched and no external provider was installed. ### Target and artifact context A self-authored empty HAR 1.2 capture, an absent path, an empty directory, a symlink to the HAR, and an argument whose final component is 300 ASCII characters. No file is created at the overlong selection. All fixture and application-state trees were removed. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence The executed recipe printed: ```text selection exit code category phase errno valid 0 success missing 1 invalid_request invalid_input directory 1 invalid_request invalid_input symlink 1 invalid_request invalid_input long-1 1 execution_failure execution_failure capture-read ENAMETOOLONG long-2 1 execution_failure execution_failure capture-read ENAMETOOLONG long-3 1 execution_failure execution_failure capture-read ENAMETOOLONG ``` Each long selection returned this exact message and remediation action: ```text Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed. ``` The full response included `retryable: false`, `details.provider_id: "har"`, `details.operation: "inspect_web_network_capture"`, `details.diagnostics.phase: "capture-read"`, the selected path, and an `ENAMETOOLONG: name too long, lstat` reason. `details.issues` was absent. The script checks these properties directly. Sources inspected at the baseline: - `docs/web-network-captures.md:961-974` distinguishes stable regular-file reads, missing-file input failures, host access denials and acquisition-time integrity failures. `docs/tool-design.md:348-351` requires expected input failures to retain caller field paths and correction metadata; `AGENTS.md`, Architecture and Cleanup, requires meaningful tagged boundary failures. - `src/browser/history/HistoricalCaptureDecoder.ts:142-168` selects the configured adapter and acquires the capture before setting phase to decoder or creating its private runtime. `src/artifacts/readStableArtifact.ts:75` performs the first `lstat`; no initial file identity has yet been admitted. - `src/browser/history/CaptureFailures.ts:191-212` handles access-denied errors separately and maps raw `ENOENT`/`ENOTDIR` during capture read to `AnalysisInputError` with a `capture_path` issue. Its fallback at lines 228-253 constructs `ProviderAdapterError` for the observed errno. - `src/artifacts/ArtifactFileOperation.ts:105-129` recognizes `ENAMETOOLONG` as a caller-selected artifact path-resolution error, distinct from physical I/O and permissions. The stable reader uses this distinction after validation for integrity failures, so the acquisition stage matters. As an additional executed control, `inspect-byte-entropy` on the identical 300-character selection returned `invalid_request` / `invalid_input`, with a `path` issue naming `ENAMETOOLONG`. That control supports the pathname diagnosis but does not define the capture provider's contract. ### Typed outcome or recovery expected `invalid_request` / `invalid_input`, with an issue identifying `capture_path` and retaining `ENAMETOOLONG` or an equivalent accurate pathname-limit explanation. Ask the caller to select a resolvable capture path rather than run provider diagnostics. Preserve the existing safe refusal and non-retryable status. ### Analysis or suspected cause Source-derived explanation: the initial `lstat` rejection reaches the capture-specific classifier as a raw host error. Its selected-path branch includes missing/not-a-directory errno values but omits `ENAMETOOLONG`, causing the provider-adapter fallback to own this input error. The CLI observations establish the wrong classification independently of a proposed implementation. No code change or repaired result was tested. ### Additional context Read-only searches covered open and closed issues, open/closed/merged PRs and issue comments. No matching historical-capture overlong-selection report was found. Related reports have different restoration conditions: - [#7607](https://github.com/morluto/rea/issues/7607), [#7629](https://github.com/morluto/rea/issues/7629), and [#7573](https://github.com/morluto/rea/issues/7573) concern reference-root recovery, process-scenario symlink loops and Evidence write resource failures respectively. - Closed [#5949](https://github.com/morluto/rea/issues/5949) and merged [PR #6371](https://github.com/morluto/rea/pull/6371) restore plist selected/default errors in a separate owner. Closed [#4188](https://github.com/morluto/rea/issues/4188), [#6740](https://github.com/morluto/rea/issues/6740), and [#6754](https://github.com/morluto/rea/issues/6754) restore SQLite, EVM and entropy classifications. Those fixes do not change this capture reader. The baseline entropy control already returns the expected input result while this capture call still fails. - Closed [#1128](https://github.com/morluto/rea/issues/1128) and [#5965](https://github.com/morluto/rea/issues/5965) concern decoder heap exhaustion and explicit reply-budget refusal after acquisition. Their resource/output closure guarantees differ from an initially unresolvable selected path. - Merged [PR #1240](https://github.com/morluto/rea/pull/1240) and [PR #1396](https://github.com/morluto/rea/pull/1396) classify export/trace selections in other readers. Merged [PR #1899](https://github.com/morluto/rea/pull/1899) adds NUL schema rejection, while [PR #946](https://github.com/morluto/rea/pull/946) addresses scenario/JSON-file boundaries. None restores this initial capture-read errno mapping. This report does not claim regression from any of those fixes. Last known good revision and regression window: NOT_ENOUGH_INFO. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Six real HAR CLI selections received provider execution-failure classification and no capture_path correction issue for a host-confirmed initial pathname-resolution error; valid, missing, directory and symlink controls establish the separate success and input outcomes. This is bounded machine-readable failure misclassification, without observed data loss or a general capture outage. - Workaround status: available - Workaround basis: The host reason remains readable in diagnostics, and the same command successfully inspects the generated HAR at a short resolvable path. Correct the path selection rather than changing provider setup; the invalid overlong name does not denote a readable capture whose contents must be recovered.
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#4561
No description provided.