[GH-ISSUE #3400] [Bug] Overflowing JavaScript timer delays abort application analysis #1264

Closed
opened 2026-10-16 21:03:32 +00:00 by bsakaguchi · 1 comment
Owner

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

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

Analyzing a local JavaScript application containing setTimeout(() => {}, 1e999); fails with unreadable_output because timer projection attempts to publish a nonfinite JSON number. The source is valid JavaScript: node --check exits 0. The public CLI exits 1, and the stdio MCP tool analyze_javascript_application returns isError: true with the same error code and reason.

The CLI retains partial Evidence in details.partial_observation, with semantic coverage marked partial. MCP retains an Evidence reference in the error's details.partial_observation. The artifact inventory survives, but the affected file's semantic facts become an incomplete module, and its timer node is absent. Adding an unrelated exported greet function still produces only one module node and zero relations, with no function nodes. Replacing only the delay with 100 returns eleven semantic nodes and two relations, including greet and the timer.

Reproduction

  1. Start from a clean checkout, select the reproduced baseline, install locked dependencies, and build the runtime.
git clone https://github.com/morluto/rea.git rea-timer-repro
cd rea-timer-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached

The observed reproduction used dependencies and a runtime already installed and built. These preparation commands supply the same prerequisites; installation and building are not additional claimed observations.

  1. From the repository root, create an isolated application directory and configuration home, check the source syntax, and run the public CLI. These commands leave repository files unchanged and provide every input inline.
rea_cli="$PWD/scripts/rea.mjs"
repro_dir=$(mktemp -d)
mkdir -p "$repro_dir/home" "$repro_dir/app"
cat > "$repro_dir/app/main.js" <<'JS'
setTimeout(() => {}, 1e999);
JS
node --check "$repro_dir/app/main.js"
env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json"
analysis_status=$?
printf 'analysis exit status: %s\n' "$analysis_status"
node -e 'const r=require(process.argv[1]); console.log(r.code); console.log(r.details.reason)' "$repro_dir/result.json"

The observed syntax check exits 0; analysis exits 1 with the diagnostic below. This single-statement fixture needs no package manifest or external engine.

  1. Replace the source with each discriminating control and rerun the same analysis command.
for delay in 100 1e308 Infinity; do
  printf 'setTimeout(() => {}, %s);\n' "$delay" > "$repro_dir/app/main.js"
  env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json"
  printf 'delay %s: analysis exit status %s\n' "$delay" "$?"
done
cat > "$repro_dir/app/main.js" <<'JS'
export const large = 1e999;
JS
env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json"
printf 'standalone literal: analysis exit status %s\n' "$?"

Each timer control was observed to exit 0 and retain a timer node with delay_milliseconds equal to 100, 1e308, or null for the Infinity identifier, respectively. The standalone export const large = 1e999; control also exits 0.

  1. Add an unrelated exported function and compare the failed file's partial semantic graph with the finite-delay control.
cat > "$repro_dir/app/main.js" <<'JS'
export function greet() { return "hello"; }
setTimeout(() => {}, 1e999);
JS
env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json"
node -e 'const r=require(process.argv[1]); const g=r.details.partial_observation.normalized_result.semantic_graph; console.log("nodes", g.nodes.length, "relations", g.relations.length); console.log("kinds", g.nodes.map(n=>n.kind))' "$repro_dir/result.json"
cat > "$repro_dir/app/main.js" <<'JS'
export function greet() { return "hello"; }
setTimeout(() => {}, 100);
JS
env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json"
node -e 'const r=require(process.argv[1]); const g=r.normalized_result.semantic_graph; console.log("nodes", g.nodes.length, "relations", g.relations.length); console.log("kinds", g.nodes.map(n=>n.kind))' "$repro_dir/result.json"

The overflowing-delay result was observed to contain only one module node and zero relations, with no function nodes. Changing only the delay to 100 returns eleven nodes and two relations, including greet and the timer.

  1. Exercise the equivalent stdio MCP operation on one connection. This inline client uses the checkout's locked MCP client dependency and starts node scripts/rea.mjs mcp with the same isolated home.
node --input-type=module - "$repro_dir" <<'JS'
import { writeFile } from 'node:fs/promises';
import { resolve } from 'node:path';
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
const root = process.argv[2];
const client = new Client({ name: 'timer-repro', version: '1' });
const transport = new StdioClientTransport({
  command: process.execPath,
  args: [resolve('scripts/rea.mjs'), 'mcp'],
  cwd: process.cwd(),
  env: { ...process.env, HOME: resolve(root, 'home') },
  stderr: 'pipe',
});
transport.stderr?.on('data', () => {});
try {
  await client.connect(transport);
  for (const delay of ['1e999', '100', 'Infinity']) {
    await writeFile(resolve(root, 'app/main.js'), `setTimeout(() => {}, ${delay});\n`);
    const result = await client.callTool({
      name: 'analyze_javascript_application',
      arguments: { input_path: resolve(root, 'app') },
    });
    console.log('delay', delay, 'isError', result.isError === true);
    console.log(JSON.stringify(result));
  }
} finally {
  await client.close();
}
JS

The equivalent calls were observed to return isError: true for the overflowing delay and successful analysis Evidence for 100 and Infinity on the same connection. The inline wrapper spells out that procedure; its formatting is not a separate observation.

Expected behavior

The analyzer should recover the valid source's static structure and timer scheduling candidate, keeping a nonrepresentable delay explicitly unknown. A nonfinite value must not cause an internal graph-validation failure or discard unrelated semantics. Graph properties must remain valid JSON; analysis must not invent a finite delay or claim runtime behavior to satisfy serialization.

docs/javascript-artifact-reconstruction.md:3-6 defines static directory analysis returning graph Evidence. docs/javascript-application-workflows.md:68-76 includes Node timer candidates in semantic extraction, and docs/javascript-application-workflows.md:96-99 preserves unsupported semantics as explicit unknowns. src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:533-539 explicitly expects overflowing numeric literals to remain unknown rather than publish non-JSON numbers.

Impact and scope

Impact level is P2, supported by the reproduced failure. A valid overflowing numeric timer delay causes a bounded but consequential analysis failure through both supported interfaces and loses the affected file's unrelated semantic facts. Partial artifact Evidence survives; no input corruption was observed. The tested reach is the supplied timer syntax, rather than all JavaScript applications.

Workaround status is unverified. The Infinity spelling succeeds as a control, but editing caller-selected source changes its artifact identity. No workaround preserving both the original source identity and the affected file's semantic analysis was verified.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1 arm64. The runtime and locked dependencies were already built and installed.

Target and artifact context

A local JavaScript application directory containing only the inline main.js source, with no package manifest. Analysis returns partial application Evidence when the timer delay overflows. No Hopper, Ghidra, Electron, browser, or application execution was required.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The CLI diagnostic and exit status were observed through the public operation; MCP returns the same error code and reason.

analysis exit status: 1
code: unreadable_output
category: execution_failure
retryable: false
message: Analysis returned an unreadable result. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.
details.reason:
main.js during projection: [
  {
    "code": "custom",
    "message": "JSON numbers must be finite",
    "path": [
      "properties",
      "delay_milliseconds"
    ]
  }
]

Evidence provenance is observed, local verification reproduced the failure, and the reproduction is complete. The following existing suites passed all 83 tests; they currently exercise successful timer projection and unknown nonfinite primitive values separately.

npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts src/domain/javascript/javascriptSemanticAnalysis.runtime_boundaries.test.ts src/application/javascript/JavaScriptSemanticGraphResources.test.ts

Typed outcome or recovery expected

Run the overflowing-delay fixture through both interfaces. The CLI should exit 0 and MCP should return successful analysis Evidence containing the timer scheduling candidate and an explicit unknown or other JSON-safe representation for the unsupported delay. The fixture with greet must retain its unrelated function semantics. Keep the finite controls' timer delays unchanged, and keep the standalone overflowing literal analyzable. Restoring success by skipping the entire file, dropping all timer facts, inventing a finite delay, or relaxing JSON validation does not restore the contract.

Analysis or suspected cause

Source-derived reasoning: src/domain/javascript/javascriptSemanticAsyncEffects.ts:331-335 returns the numeric literal's value directly, and src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts:159-168 projects it into timer properties. src/domain/jsonValue.ts:69 rejects nonfinite JSON numbers. This explains the suspected path from an overflowing source literal to invalid delay_milliseconds; the CLI and MCP reproductions supply the observed failure evidence.

Additional context

Related #3358 concerns resolved cancellation links after timer-handle reassignment; this report concerns a nonfinite delay aborting projection for an ordinary timer.

Related #3367 concerns false built-in effects after globals are replaced; this report reproduces with the original setTimeout binding.

Related completed #3128 concerns an unsupported interpretation field on candidate endpoints; this report concerns a nonfinite numeric timer property rejected during semantic projection.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3400 ### 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 Analyzing a local JavaScript application containing `setTimeout(() => {}, 1e999);` fails with `unreadable_output` because timer projection attempts to publish a nonfinite JSON number. The source is valid JavaScript: `node --check` exits 0. The public CLI exits 1, and the stdio MCP tool `analyze_javascript_application` returns `isError: true` with the same error code and reason. The CLI retains partial Evidence in `details.partial_observation`, with semantic coverage marked partial. MCP retains an Evidence reference in the error's `details.partial_observation`. The artifact inventory survives, but the affected file's semantic facts become an incomplete module, and its timer node is absent. Adding an unrelated exported `greet` function still produces only one module node and zero relations, with no function nodes. Replacing only the delay with `100` returns eleven semantic nodes and two relations, including `greet` and the timer. ### Reproduction 1. Start from a clean checkout, select the reproduced baseline, install locked dependencies, and build the runtime. ```bash git clone https://github.com/morluto/rea.git rea-timer-repro cd rea-timer-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` The observed reproduction used dependencies and a runtime already installed and built. These preparation commands supply the same prerequisites; installation and building are not additional claimed observations. 2. From the repository root, create an isolated application directory and configuration home, check the source syntax, and run the public CLI. These commands leave repository files unchanged and provide every input inline. ```bash rea_cli="$PWD/scripts/rea.mjs" repro_dir=$(mktemp -d) mkdir -p "$repro_dir/home" "$repro_dir/app" cat > "$repro_dir/app/main.js" <<'JS' setTimeout(() => {}, 1e999); JS node --check "$repro_dir/app/main.js" env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json" analysis_status=$? printf 'analysis exit status: %s\n' "$analysis_status" node -e 'const r=require(process.argv[1]); console.log(r.code); console.log(r.details.reason)' "$repro_dir/result.json" ``` The observed syntax check exits 0; analysis exits 1 with the diagnostic below. This single-statement fixture needs no package manifest or external engine. 3. Replace the source with each discriminating control and rerun the same analysis command. ```bash for delay in 100 1e308 Infinity; do printf 'setTimeout(() => {}, %s);\n' "$delay" > "$repro_dir/app/main.js" env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json" printf 'delay %s: analysis exit status %s\n' "$delay" "$?" done cat > "$repro_dir/app/main.js" <<'JS' export const large = 1e999; JS env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json" printf 'standalone literal: analysis exit status %s\n' "$?" ``` Each timer control was observed to exit 0 and retain a timer node with `delay_milliseconds` equal to `100`, `1e308`, or `null` for the `Infinity` identifier, respectively. The standalone `export const large = 1e999;` control also exits 0. 4. Add an unrelated exported function and compare the failed file's partial semantic graph with the finite-delay control. ```bash cat > "$repro_dir/app/main.js" <<'JS' export function greet() { return "hello"; } setTimeout(() => {}, 1e999); JS env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json" node -e 'const r=require(process.argv[1]); const g=r.details.partial_observation.normalized_result.semantic_graph; console.log("nodes", g.nodes.length, "relations", g.relations.length); console.log("kinds", g.nodes.map(n=>n.kind))' "$repro_dir/result.json" cat > "$repro_dir/app/main.js" <<'JS' export function greet() { return "hello"; } setTimeout(() => {}, 100); JS env HOME="$repro_dir/home" node "$rea_cli" analyze-javascript-application "$repro_dir/app" --json > "$repro_dir/result.json" node -e 'const r=require(process.argv[1]); const g=r.normalized_result.semantic_graph; console.log("nodes", g.nodes.length, "relations", g.relations.length); console.log("kinds", g.nodes.map(n=>n.kind))' "$repro_dir/result.json" ``` The overflowing-delay result was observed to contain only one module node and zero relations, with no function nodes. Changing only the delay to `100` returns eleven nodes and two relations, including `greet` and the timer. 5. Exercise the equivalent stdio MCP operation on one connection. This inline client uses the checkout's locked MCP client dependency and starts `node scripts/rea.mjs mcp` with the same isolated home. ```bash node --input-type=module - "$repro_dir" <<'JS' import { writeFile } from 'node:fs/promises'; import { resolve } from 'node:path'; import { Client } from '@modelcontextprotocol/client'; import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; const root = process.argv[2]; const client = new Client({ name: 'timer-repro', version: '1' }); const transport = new StdioClientTransport({ command: process.execPath, args: [resolve('scripts/rea.mjs'), 'mcp'], cwd: process.cwd(), env: { ...process.env, HOME: resolve(root, 'home') }, stderr: 'pipe', }); transport.stderr?.on('data', () => {}); try { await client.connect(transport); for (const delay of ['1e999', '100', 'Infinity']) { await writeFile(resolve(root, 'app/main.js'), `setTimeout(() => {}, ${delay});\n`); const result = await client.callTool({ name: 'analyze_javascript_application', arguments: { input_path: resolve(root, 'app') }, }); console.log('delay', delay, 'isError', result.isError === true); console.log(JSON.stringify(result)); } } finally { await client.close(); } JS ``` The equivalent calls were observed to return `isError: true` for the overflowing delay and successful analysis Evidence for `100` and `Infinity` on the same connection. The inline wrapper spells out that procedure; its formatting is not a separate observation. ### Expected behavior The analyzer should recover the valid source's static structure and timer scheduling candidate, keeping a nonrepresentable delay explicitly unknown. A nonfinite value must not cause an internal graph-validation failure or discard unrelated semantics. Graph properties must remain valid JSON; analysis must not invent a finite delay or claim runtime behavior to satisfy serialization. `docs/javascript-artifact-reconstruction.md:3-6` defines static directory analysis returning graph Evidence. `docs/javascript-application-workflows.md:68-76` includes Node timer candidates in semantic extraction, and `docs/javascript-application-workflows.md:96-99` preserves unsupported semantics as explicit unknowns. `src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:533-539` explicitly expects overflowing numeric literals to remain unknown rather than publish non-JSON numbers. ### Impact and scope Impact level is P2, supported by the reproduced failure. A valid overflowing numeric timer delay causes a bounded but consequential analysis failure through both supported interfaces and loses the affected file's unrelated semantic facts. Partial artifact Evidence survives; no input corruption was observed. The tested reach is the supplied timer syntax, rather than all JavaScript applications. Workaround status is unverified. The `Infinity` spelling succeeds as a control, but editing caller-selected source changes its artifact identity. No workaround preserving both the original source identity and the affected file's semantic analysis was verified. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1 arm64. The runtime and locked dependencies were already built and installed. ### Target and artifact context A local JavaScript application directory containing only the inline `main.js` source, with no package manifest. Analysis returns partial application Evidence when the timer delay overflows. No Hopper, Ghidra, Electron, browser, or application execution was required. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The CLI diagnostic and exit status were observed through the public operation; MCP returns the same error code and reason. ```text analysis exit status: 1 code: unreadable_output category: execution_failure retryable: false message: Analysis returned an unreadable result. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed. details.reason: main.js during projection: [ { "code": "custom", "message": "JSON numbers must be finite", "path": [ "properties", "delay_milliseconds" ] } ] ``` Evidence provenance is observed, local verification reproduced the failure, and the reproduction is complete. The following existing suites passed all 83 tests; they currently exercise successful timer projection and unknown nonfinite primitive values separately. ```bash npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts src/domain/javascript/javascriptSemanticAnalysis.runtime_boundaries.test.ts src/application/javascript/JavaScriptSemanticGraphResources.test.ts ``` ### Typed outcome or recovery expected Run the overflowing-delay fixture through both interfaces. The CLI should exit 0 and MCP should return successful analysis Evidence containing the timer scheduling candidate and an explicit unknown or other JSON-safe representation for the unsupported delay. The fixture with `greet` must retain its unrelated function semantics. Keep the finite controls' timer delays unchanged, and keep the standalone overflowing literal analyzable. Restoring success by skipping the entire file, dropping all timer facts, inventing a finite delay, or relaxing JSON validation does not restore the contract. ### Analysis or suspected cause Source-derived reasoning: `src/domain/javascript/javascriptSemanticAsyncEffects.ts:331-335` returns the numeric literal's value directly, and `src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts:159-168` projects it into timer properties. `src/domain/jsonValue.ts:69` rejects nonfinite JSON numbers. This explains the suspected path from an overflowing source literal to invalid `delay_milliseconds`; the CLI and MCP reproductions supply the observed failure evidence. ### Additional context Related [#3358](https://github.com/morluto/rea/issues/3358) concerns resolved cancellation links after timer-handle reassignment; this report concerns a nonfinite delay aborting projection for an ordinary timer. Related [#3367](https://github.com/morluto/rea/issues/3367) concerns false built-in effects after globals are replaced; this report reproduces with the original `setTimeout` binding. Related completed [#3128](https://github.com/morluto/rea/issues/3128) concerns an unsupported `interpretation` field on candidate endpoints; this report concerns a nonfinite numeric timer property rejected during semantic projection.
Author
Owner

@x1mm4nu3l commented on GitHub (Oct 20, 2026):

Still reproduces on main 2538214e2, Linux/Node 24.18.0, through CLI and stdio MCP. The greet-plus-setTimeout fixture passes node --check, but delay 1e999 returns unreadable_output and retains only the module node. Delay 100 preserves 11 semantic nodes; 1e308 and the Infinity identifier also succeed. After the failing MCP call, ping and finite-delay analysis succeed on the same connection, so the failure stays isolated to that analysis.

<!-- gh-comment-id:6286507151 --> @x1mm4nu3l commented on GitHub (Oct 20, 2026): Still reproduces on main 2538214e2, Linux/Node 24.18.0, through CLI and stdio MCP. The greet-plus-`setTimeout` fixture passes `node --check`, but delay 1e999 returns unreadable_output and retains only the module node. Delay 100 preserves 11 semantic nodes; 1e308 and the Infinity identifier also succeed. After the failing MCP call, ping and finite-delay analysis succeed on the same connection, so the failure stays isolated to that analysis.
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#1264
No description provided.