[GH-ISSUE #7521] [Bug] A 256-byte script URL basename prevents export of the entire capture #4468

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

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

A saved scenario capture contains two retained JavaScript responses: an ordinary regular.js URL and a URL with a 256-byte ASCII basename. Running node scripts/rea.mjs export-web-scripts "$W/capture-256.json" "$W/failure-1" --json returns exit 1 with artifact_operation_failed, category execution_failure, and reason path. The destination directory is absent afterward, so neither script nor a manifest is available from the export.

An otherwise equivalent capture whose second basename is 255 bytes exports both scripts successfully. The longer URL is accepted as capture input, but the exporter fails when trying to create its local filename.

Reproduction

Use the baseline checkout with its dependencies and compiled runtime already available, with Node.js v24.22.1 on PATH. No browser or network service is needed.

  1. Create isolated configuration directories and two valid saved captures. The baseline's fixture helper constructs the scenario envelope; the URLs and retained source bytes are specified inline:
export W=$(mktemp -d)
mkdir -p "$W/home" "$W/config" "$W/cache" "$W/data" "$W/state"
export HOME="$W/home" XDG_CONFIG_HOME="$W/config" XDG_CACHE_HOME="$W/cache" XDG_DATA_HOME="$W/data" XDG_STATE_HOME="$W/state"
node --import tsx --input-type=module <<'JS'
import { writeFileSync } from 'node:fs';
import { scriptScenarioFixture } from './tests/fixtures/webScriptCapture.ts';
for (const length of [255, 256]) {
  const capture = scriptScenarioFixture([
    { url: 'https://verification.test/regular.js', bytes: Buffer.from('export const regular = 1;\n') },
    { url: `https://verification.test/${'b'.repeat(length - 3)}.js`, bytes: Buffer.from('export const long = 2;\n') },
  ]);
  writeFileSync(`${process.env.W}/capture-${length}.json`, JSON.stringify(capture));
}
JS
  1. Invoke the public CLI with an absent destination each time. This driver prints the exit code, whether output remains, and either the persisted layouts or the error:
node --input-type=module <<'JS'
import { spawnSync } from 'node:child_process';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
for (const [length, name] of [[255, 'control'], [256, 'failure-1'], [256, 'failure-2']]) {
  const output = `${process.env.W}/${name}`;
  const result = spawnSync(process.execPath, ['scripts/rea.mjs', 'export-web-scripts', `${process.env.W}/capture-${length}.json`, output, '--json'], { encoding: 'utf8' });
  writeFileSync(`${process.env.W}/${name}.stdout`, result.stdout);
  const response = JSON.parse(result.stdout);
  const manifest = existsSync(`${output}/manifest.json`) ? JSON.parse(readFileSync(`${output}/manifest.json`, 'utf8')) : null;
  console.log(JSON.stringify({ name, basename_bytes: length, exit: result.status, output_exists: existsSync(output), code: response.code ?? null, reason: response.details?.reason ?? null, layouts: manifest?.scripts.map(s => s.content.layout) ?? null }));
  if (result.status !== 0) console.log(response.message.replaceAll(process.env.W, '$W'));
}
JS
  1. Observe the successful 255-byte control and two failed 256-byte exports. Both failing destinations remain absent. The recipe above reproduced the failure twice in two attempts. A separate boundary sweep also succeeded at 253 and 255 bytes and failed at 256 and 257 bytes.

Expected behavior

Export both retained scripts and the manifest. A source URL whose path cannot be represented on the destination filesystem should receive a unique isolated filename with an explanatory layout reason, while the ordinary script remains exportable. The export guide's Sources and mappings contract explicitly includes unrepresentable paths in isolated layouts; exact locators are listed under Evidence below.

The documented removal of partial output on actual publication failure is working in this reproduction. The defect is selecting an unrepresentable canonical filename rather than the promised isolated representation.

Impact and scope

P2: one oversized script URL component prevents the complete capture from being exported, including its ordinary script and analysis input. The original saved capture remains intact; this reproduction does not show corruption or loss of its retained bytes. No safe evidence-preserving workaround was verified. Removing the long source would discard evidence.

Observed through the CLI on macOS/APFS with a synthetic, schema-valid capture. Other filesystems, actual browser capture, MCP execution and downstream analysis were not tested. The 255/256-byte boundary is an observation on this host, not a claim about every filesystem.

REA version

Exact output of node scripts/rea.mjs --version:

6.8.0

Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; Darwin arm64; APFS destination. The reproduction uses the checkout's prebuilt CLI and installed tsx; no analysis provider runs.

Target and artifact context

Normalized saved browser-scenario capture with two script request/response transactions and retained response-body bytes. Both URLs use the synthetic verification.test origin.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Actual summary output from the inline CLI driver:

{"name":"control","basename_bytes":255,"exit":0,"output_exists":true,"code":null,"reason":null,"layouts":["url-path","url-path"]}
{"name":"failure-1","basename_bytes":256,"exit":1,"output_exists":false,"code":"artifact_operation_failed","reason":"path","layouts":null}
{"name":"failure-2","basename_bytes":256,"exit":1,"output_exists":false,"code":"artifact_operation_failed","reason":"path","layouts":null}

Both failures report Could not exclusively create extraction path: files/origins/…/bbb…bbb.js. The ellipses here abbreviate the generated origin hash and the 256-byte basename; the driver prints the complete diagnostic and retains complete JSON stdout in $W.

At the baseline, docs/website-script-export.md:59–65 defines canonical URL layouts and isolated names for unrepresentable paths. src/cli/webScriptCommands.ts:31–57 routes the supported CLI into the application workflow. src/domain/webScriptExportPlan.ts:73–107 accepts ASCII path components without checking their length. src/browser/assets/PublishWebScripts.ts:80–84 publishes planned records sequentially, and lines 207–225 limit the isolated retry to destination case collisions. Its catch at lines 129–186 releases owned partial output on failure. The write failure message originates at src/artifacts/SafeOutputTree.ts:276.

The missing final output was directly observed. The order of intermediate writes and rollback is established by source inspection, without claiming a separate observation of intermediate files.

Typed outcome or recovery expected

A successful export with both source records, a persisted manifest, and an isolated layout with a reason for the oversized path. Existing output-directory protections and exact retained bytes should remain preserved.

Analysis or suspected cause

Source-derived explanation: the planner treats the long ASCII component as an ordinary portable module path. When the real destination cannot create that filename, the publisher's case-collision-only isolation retry does not apply, and publication fails. The successful 255-byte control and failing 256-byte case support a filename-component limit as the trigger. No fix was implemented or tested.

Additional context

Open and closed issue/PR searches found related reports concerning cancellation (#5956, #6042, #6043, #6108), existing-destination admission (#6029), and case-isolation behavior/documentation (#1490, #6242). Closed #5480 and #5522 concern other export workflows' pathname failures and diagnostics; #6764 concerns sigrok snapshot naming. None has the same closure condition of isolating an oversized captured script URL so all retained scripts export successfully. No matching fix or duplicate was found. Introducing revision and last known good release are unknown.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7521 ### 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 A saved scenario capture contains two retained JavaScript responses: an ordinary `regular.js` URL and a URL with a 256-byte ASCII basename. Running `node scripts/rea.mjs export-web-scripts "$W/capture-256.json" "$W/failure-1" --json` returns exit 1 with `artifact_operation_failed`, category `execution_failure`, and reason `path`. The destination directory is absent afterward, so neither script nor a manifest is available from the export. An otherwise equivalent capture whose second basename is 255 bytes exports both scripts successfully. The longer URL is accepted as capture input, but the exporter fails when trying to create its local filename. ### Reproduction Use the baseline checkout with its dependencies and compiled runtime already available, with Node.js v24.22.1 on PATH. No browser or network service is needed. 1. Create isolated configuration directories and two valid saved captures. The baseline's fixture helper constructs the scenario envelope; the URLs and retained source bytes are specified inline: ```sh export W=$(mktemp -d) mkdir -p "$W/home" "$W/config" "$W/cache" "$W/data" "$W/state" export HOME="$W/home" XDG_CONFIG_HOME="$W/config" XDG_CACHE_HOME="$W/cache" XDG_DATA_HOME="$W/data" XDG_STATE_HOME="$W/state" node --import tsx --input-type=module <<'JS' import { writeFileSync } from 'node:fs'; import { scriptScenarioFixture } from './tests/fixtures/webScriptCapture.ts'; for (const length of [255, 256]) { const capture = scriptScenarioFixture([ { url: 'https://verification.test/regular.js', bytes: Buffer.from('export const regular = 1;\n') }, { url: `https://verification.test/${'b'.repeat(length - 3)}.js`, bytes: Buffer.from('export const long = 2;\n') }, ]); writeFileSync(`${process.env.W}/capture-${length}.json`, JSON.stringify(capture)); } JS ``` 2. Invoke the public CLI with an absent destination each time. This driver prints the exit code, whether output remains, and either the persisted layouts or the error: ```sh node --input-type=module <<'JS' import { spawnSync } from 'node:child_process'; import { existsSync, readFileSync, writeFileSync } from 'node:fs'; for (const [length, name] of [[255, 'control'], [256, 'failure-1'], [256, 'failure-2']]) { const output = `${process.env.W}/${name}`; const result = spawnSync(process.execPath, ['scripts/rea.mjs', 'export-web-scripts', `${process.env.W}/capture-${length}.json`, output, '--json'], { encoding: 'utf8' }); writeFileSync(`${process.env.W}/${name}.stdout`, result.stdout); const response = JSON.parse(result.stdout); const manifest = existsSync(`${output}/manifest.json`) ? JSON.parse(readFileSync(`${output}/manifest.json`, 'utf8')) : null; console.log(JSON.stringify({ name, basename_bytes: length, exit: result.status, output_exists: existsSync(output), code: response.code ?? null, reason: response.details?.reason ?? null, layouts: manifest?.scripts.map(s => s.content.layout) ?? null })); if (result.status !== 0) console.log(response.message.replaceAll(process.env.W, '$W')); } JS ``` 3. Observe the successful 255-byte control and two failed 256-byte exports. Both failing destinations remain absent. The recipe above reproduced the failure twice in two attempts. A separate boundary sweep also succeeded at 253 and 255 bytes and failed at 256 and 257 bytes. ### Expected behavior Export both retained scripts and the manifest. A source URL whose path cannot be represented on the destination filesystem should receive a unique isolated filename with an explanatory layout reason, while the ordinary script remains exportable. The export guide's Sources and mappings contract explicitly includes unrepresentable paths in isolated layouts; exact locators are listed under Evidence below. The documented removal of partial output on actual publication failure is working in this reproduction. The defect is selecting an unrepresentable canonical filename rather than the promised isolated representation. ### Impact and scope P2: one oversized script URL component prevents the complete capture from being exported, including its ordinary script and analysis input. The original saved capture remains intact; this reproduction does not show corruption or loss of its retained bytes. No safe evidence-preserving workaround was verified. Removing the long source would discard evidence. Observed through the CLI on macOS/APFS with a synthetic, schema-valid capture. Other filesystems, actual browser capture, MCP execution and downstream analysis were not tested. The 255/256-byte boundary is an observation on this host, not a claim about every filesystem. ### REA version Exact output of `node scripts/rea.mjs --version`: ```text 6.8.0 ``` Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js `v24.22.1`; macOS `27.0.1`; Darwin arm64; APFS destination. The reproduction uses the checkout's prebuilt CLI and installed `tsx`; no analysis provider runs. ### Target and artifact context Normalized saved browser-scenario capture with two script request/response transactions and retained response-body bytes. Both URLs use the synthetic `verification.test` origin. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence Actual summary output from the inline CLI driver: ```jsonl {"name":"control","basename_bytes":255,"exit":0,"output_exists":true,"code":null,"reason":null,"layouts":["url-path","url-path"]} {"name":"failure-1","basename_bytes":256,"exit":1,"output_exists":false,"code":"artifact_operation_failed","reason":"path","layouts":null} {"name":"failure-2","basename_bytes":256,"exit":1,"output_exists":false,"code":"artifact_operation_failed","reason":"path","layouts":null} ``` Both failures report `Could not exclusively create extraction path: files/origins/…/bbb…bbb.js`. The ellipses here abbreviate the generated origin hash and the 256-byte basename; the driver prints the complete diagnostic and retains complete JSON stdout in `$W`. At the baseline, `docs/website-script-export.md:59–65` defines canonical URL layouts and isolated names for unrepresentable paths. `src/cli/webScriptCommands.ts:31–57` routes the supported CLI into the application workflow. `src/domain/webScriptExportPlan.ts:73–107` accepts ASCII path components without checking their length. `src/browser/assets/PublishWebScripts.ts:80–84` publishes planned records sequentially, and lines 207–225 limit the isolated retry to destination case collisions. Its catch at lines 129–186 releases owned partial output on failure. The write failure message originates at `src/artifacts/SafeOutputTree.ts:276`. The missing final output was directly observed. The order of intermediate writes and rollback is established by source inspection, without claiming a separate observation of intermediate files. ### Typed outcome or recovery expected A successful export with both source records, a persisted manifest, and an isolated layout with a reason for the oversized path. Existing output-directory protections and exact retained bytes should remain preserved. ### Analysis or suspected cause Source-derived explanation: the planner treats the long ASCII component as an ordinary portable module path. When the real destination cannot create that filename, the publisher's case-collision-only isolation retry does not apply, and publication fails. The successful 255-byte control and failing 256-byte case support a filename-component limit as the trigger. No fix was implemented or tested. ### Additional context Open and closed issue/PR searches found related reports concerning cancellation (#5956, #6042, #6043, #6108), existing-destination admission (#6029), and case-isolation behavior/documentation (#1490, #6242). Closed #5480 and #5522 concern other export workflows' pathname failures and diagnostics; #6764 concerns sigrok snapshot naming. None has the same closure condition of isolating an oversized captured script URL so all retained scripts export successfully. No matching fix or duplicate was found. Introducing revision and last known good release are unknown.
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#4468
No description provided.