[GH-ISSUE #2826] [Bug] Browser source-map collection rejects valid maps without optional names #1010

Closed
opened 2026-10-15 18:30:19 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @shoyann on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2826

Before submitting

  • Searched open/closed issues and recent PRs for related reports.
  • Removed credentials, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction.

Summary

Browser source-map collection rejects a valid version 3 source map when the optional names field is omitted. Adding only "names": [] makes the same HTTP response collect successfully. This discards the map artifact, original source, and mapping information even though no mapping refers to a name.

Reproduction

From a REA source checkout at 9680da76c84a6833635dc7a5df607806d899f804, with the pinned Node.js 24.18.0 / npm 11.16.0 toolchain:

npm ci
npm run build:cached
node --input-type=module <<'NODE'
import { createServer } from 'node:http';
import { once } from 'node:events';
import { fetchWebSourceMaps } from './dist/browser/WebSourceMapFetcher.js';
import { analyzeWebBundleInputSchema } from './dist/domain/webBundleAnalysis.js';

const map = {
  version: 3,
  sources: ['original.js'],
  sourcesContent: ['export const answer = 42;'],
  mappings: 'AAAA',
};
const fixtures = { omitted: map, explicit: { ...map, names: [] } };
const server = createServer((request, response) => {
  response.setHeader('content-type', 'application/source-map+json');
  response.end(JSON.stringify(fixtures[request.url.slice(1)]));
});
const listening = once(server, 'listening');
server.listen(0, '127.0.0.1');
await listening;
try {
  const origin = `http://127.0.0.1:${server.address().port}`;
  const input = analyzeWebBundleInputSchema.parse({
    cdp_endpoint: 'http://127.0.0.1:9222',
    target_id: 'source-map-fixture',
    allowed_origins: [origin],
    fetch_source_maps: true,
  });
  const names = Object.keys(fixtures);
  const result = await fetchWebSourceMaps(names.map(name => ({
    scriptKey: `scr_${'1'.repeat(64)}`,
    declaredUrl: `${origin}/${name}`,
    fetchUrl: `${origin}/${name}`,
  })), input);
  result.items.forEach((item, index) => console.log(JSON.stringify({
    fixture: names[index],
    status: item.status,
    artifactRetained: item.artifact !== null,
    originalSources: item.original_sources.length,
    mappings: item.mappings.length,
    limitation: item.limitation,
  })));
} finally {
  server.closeAllConnections();
  await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve()));
}
NODE

This invokes the production collector with real loopback HTTP responses. It does not connect to a CDP endpoint or require Chrome; the CDP fields only satisfy the operation input schema.

Actual behavior

  • omitted: status: "invalid", no retained map artifact, 0 original sources, 0 mappings, limitation Source-map JSON mappings could not be decoded safely.
  • explicit: status: "included", retained artifact, 1 original source, 1 mapping.

The same failure occurs when the omitted-names leaf is embedded in an indexed source map. A boundary regression on the above revision fails for both regular and indexed maps, while its explicit-empty-array control passes.

Expected behavior

Both fixtures should be included with the same decoded source and mapping, while retaining their different original response bytes. ECMA-426 section 9 defines names as optional. Omission supplies no symbol names; it does not invalidate a four-component mapping such as AAAA.

Malformed present values and out-of-range name references should continue to be rejected.

Impact and scope

This affects browser source-map collection for otherwise valid producer output. The collector drops recoverable original-source evidence and mappings. It is not a fetch, browser attachment, or authentication failure.

Environment

  • REA source: 9680da76c84a6833635dc7a5df607806d899f804
  • Node.js: 24.18.0; npm: 11.16.0
  • Platform: Linux x64, Ubuntu under WSL
  • Interface: production browser collector invoked directly over real HTTP
  • Earliest failure stage: source-map format validation after a successful fetch
  • Target: source-owned minimal JSON source maps; no external provider required

Source-derived cause

SourceMapFormat.ts already declares names: z.array(z.string()).default([]) in the shared leaf schema. Before that schema runs, the browser-collection profile independently requires Array.isArray(rawNames), rejecting omission. Removing this redundant profile-specific requirement should allow the shared schema to own omission and present-value validation; decoded name-index checks must remain in force.

Originally created by @shoyann on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2826 ## Before submitting - [x] Searched open/closed issues and recent PRs for related reports. - [x] Removed credentials, private paths, and sensitive target data. ## Area Evidence, artifacts, or reconstruction. ## Summary Browser source-map collection rejects a valid version 3 source map when the optional `names` field is omitted. Adding only `"names": []` makes the same HTTP response collect successfully. This discards the map artifact, original source, and mapping information even though no mapping refers to a name. ## Reproduction From a REA source checkout at `9680da76c84a6833635dc7a5df607806d899f804`, with the pinned Node.js 24.18.0 / npm 11.16.0 toolchain: ```sh npm ci npm run build:cached node --input-type=module <<'NODE' import { createServer } from 'node:http'; import { once } from 'node:events'; import { fetchWebSourceMaps } from './dist/browser/WebSourceMapFetcher.js'; import { analyzeWebBundleInputSchema } from './dist/domain/webBundleAnalysis.js'; const map = { version: 3, sources: ['original.js'], sourcesContent: ['export const answer = 42;'], mappings: 'AAAA', }; const fixtures = { omitted: map, explicit: { ...map, names: [] } }; const server = createServer((request, response) => { response.setHeader('content-type', 'application/source-map+json'); response.end(JSON.stringify(fixtures[request.url.slice(1)])); }); const listening = once(server, 'listening'); server.listen(0, '127.0.0.1'); await listening; try { const origin = `http://127.0.0.1:${server.address().port}`; const input = analyzeWebBundleInputSchema.parse({ cdp_endpoint: 'http://127.0.0.1:9222', target_id: 'source-map-fixture', allowed_origins: [origin], fetch_source_maps: true, }); const names = Object.keys(fixtures); const result = await fetchWebSourceMaps(names.map(name => ({ scriptKey: `scr_${'1'.repeat(64)}`, declaredUrl: `${origin}/${name}`, fetchUrl: `${origin}/${name}`, })), input); result.items.forEach((item, index) => console.log(JSON.stringify({ fixture: names[index], status: item.status, artifactRetained: item.artifact !== null, originalSources: item.original_sources.length, mappings: item.mappings.length, limitation: item.limitation, }))); } finally { server.closeAllConnections(); await new Promise((resolve, reject) => server.close(error => error ? reject(error) : resolve())); } NODE ``` This invokes the production collector with real loopback HTTP responses. It does not connect to a CDP endpoint or require Chrome; the CDP fields only satisfy the operation input schema. ## Actual behavior - `omitted`: `status: "invalid"`, no retained map artifact, 0 original sources, 0 mappings, limitation `Source-map JSON mappings could not be decoded safely.` - `explicit`: `status: "included"`, retained artifact, 1 original source, 1 mapping. The same failure occurs when the omitted-`names` leaf is embedded in an indexed source map. A boundary regression on the above revision fails for both regular and indexed maps, while its explicit-empty-array control passes. ## Expected behavior Both fixtures should be included with the same decoded source and mapping, while retaining their different original response bytes. [ECMA-426 section 9](https://tc39.es/ecma426/#sec-source-map-format) defines `names` as optional. Omission supplies no symbol names; it does not invalidate a four-component mapping such as `AAAA`. Malformed present values and out-of-range name references should continue to be rejected. ## Impact and scope This affects browser source-map collection for otherwise valid producer output. The collector drops recoverable original-source evidence and mappings. It is not a fetch, browser attachment, or authentication failure. ## Environment - REA source: `9680da76c84a6833635dc7a5df607806d899f804` - Node.js: 24.18.0; npm: 11.16.0 - Platform: Linux x64, Ubuntu under WSL - Interface: production browser collector invoked directly over real HTTP - Earliest failure stage: source-map format validation after a successful fetch - Target: source-owned minimal JSON source maps; no external provider required ## Source-derived cause `SourceMapFormat.ts` already declares `names: z.array(z.string()).default([])` in the shared leaf schema. Before that schema runs, the `browser-collection` profile independently requires `Array.isArray(rawNames)`, rejecting omission. Removing this redundant profile-specific requirement should allow the shared schema to own omission and present-value validation; decoded name-index checks must remain in force.
bsakaguchi 2026-10-15 18:30:19 +00:00
  • closed this issue
  • added the
    bug
    label
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#1010
No description provided.