[GH-ISSUE #1387] 6.3.0: analyze-javascript-application --json aborts on a large bundle that 6.2.0 completes #481

Closed
opened 2026-10-15 18:25:26 +00:00 by bsakaguchi · 7 comments
Owner

Originally created by @yemudo on GitHub (Oct 10, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/1387

Summary: On the same input, analyze-javascript-application --json completes on 6.2.0 but aborts on 6.3.0 (Node Aborted, core dumped, exit 134).

Input: a production web app bundle: 10 files, about 15 MB of minified JavaScript, 3 parsed JS files.

version result time output
6.2.0 completes ~232 s 1,843 findings, ~41.6k semantic-graph nodes, ~500 MB JSON
6.3.0, default heap aborts 91 s none
6.3.0, NODE_OPTIONS=--max-old-space-size=5120 aborts 253 s none

A tiny bundle completes fine on 6.3.0.

Environment: Linux x64, Node 22.23.3, 4 vCPU, 7 GB RAM. CLI run with --json to stdout.

Suspected cause: 6.3.0 adds evidence contexts per semantic fact. Our 6.2.0 report was already about 500 MB, so the 6.3.0 JSON likely exceeds V8's maximum string length during serialization. More heap doesn't help. A streaming JSON writer, or an option to omit the full semantic graph or write it to a file, would fix it.

Repro: any bundle large enough that the 6.2.0 report approaches 500 MB.

We can't share the bundle (private application), but we're happy to test a pre-release build and report back.

Originally created by @yemudo on GitHub (Oct 10, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/1387 **Summary:** On the same input, `analyze-javascript-application --json` completes on 6.2.0 but aborts on 6.3.0 (Node `Aborted`, core dumped, exit 134). **Input:** a production web app bundle: 10 files, about 15 MB of minified JavaScript, 3 parsed JS files. | version | result | time | output | |---|---|---|---| | 6.2.0 | completes | ~232 s | 1,843 findings, ~41.6k semantic-graph nodes, ~500 MB JSON | | 6.3.0, default heap | aborts | 91 s | none | | 6.3.0, `NODE_OPTIONS=--max-old-space-size=5120` | aborts | 253 s | none | A tiny bundle completes fine on 6.3.0. **Environment:** Linux x64, Node 22.23.3, 4 vCPU, 7 GB RAM. CLI run with `--json` to stdout. **Suspected cause:** 6.3.0 adds evidence contexts per semantic fact. Our 6.2.0 report was already about 500 MB, so the 6.3.0 JSON likely exceeds V8's maximum string length during serialization. More heap doesn't help. A streaming JSON writer, or an option to omit the full semantic graph or write it to a file, would fix it. **Repro:** any bundle large enough that the 6.2.0 report approaches 500 MB. We can't share the bundle (private application), but we're happy to test a pre-release build and report back.
bsakaguchi 2026-10-15 18:25:26 +00:00
Author
Owner

@CassieFox23 commented on GitHub (Oct 10, 2026):

TL;DR: I could not make 6.3.0 crash where 6.2.0 works. On two big public bundles, 6.3.0 used less memory. It also wrote smaller JSON.

What I ran

  • Command: rea analyze-javascript-application <asar> --json > out.json.
  • I timed each run with /usr/bin/time -v.
  • Node's default heap here is 4144 MB.
  • I also capped the heap with --max-old-space-size.

Bundle A: open source

I used a copy of the Beyond All Reason launcher (launcher code MIT; Flathub package GPL-2.0-or-later). Its Flathub id is info.beyondallreason.bar. It is version 1.2988.0.3. File: app.asar, 26,870,695 bytes.
sha256: 595810df454127cd45783dca6fa0e139b70a22e435eace530816a35d83ad6f03

REA parsed 2,027 source files here.

version heap cap result time max RSS (GiB) JSON
6.2.0 default ok 67 s 2.0 563 MB
6.3.0 default ok 52 s 1.9 402 MB
6.3.0 2048 MB ok 53 s 1.4 402 MB
6.3.0 1024 MB ok 53 s 1.0 402 MB

Both versions found 11,666 findings.

Bundle B: closed source

This is a 27 MB proprietary Electron bundle from Flathub. It is closer to your shape. Most of it is minified code. REA parsed 38 source files.

version heap cap result time max RSS (GiB) JSON
6.2.0 default ok 143 s 2.7 890 MB
6.3.0 default ok 139 s 2.7 562 MB
6.2.0 2048 MB ok 132 s 2.0 890 MB
6.3.0 2048 MB ok 132 s 1.8 562 MB
6.2.0 1536 MB ok 130 s 1.6 890 MB
6.3.0 1536 MB ok 132 s 1.5 562 MB
6.2.0 1024 MB abort, 134 30 s 1.2 none
6.3.0 1024 MB abort, 134 67 s 1.2 none

Both versions found 1,464 findings. The four default-heap outputs parsed as JSON. Capped runs matched them in size, to the byte.

At 1024 MB, both versions crashed the same way:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

That is a plain heap limit. It is not "Invalid string length." Both crashed while still parsing. The JSON file was still empty. 6.2.0 died on file 1 of 38. 6.3.0 got to file 17 of 38.

What this rules out

  • 6.2.0 wrote 890 MB of JSON fine.
  • 6.3.0 wrote 562 MB fine. That is past your 500 MB.
  • On these bundles, 6.3.0 needs no more heap than 6.2.0.
  • The new evidence contexts did not grow the output. 6.3.0 JSON was 29% to 37% smaller.

What it does not rule out

Your bundle has a different shape. It gave about 41.6k graph nodes. Both of mine hit the 100,000 node ceiling. So your crash may take another path.

Your exit code 134 matches a V8 fatal error. A string limit would usually throw a RangeError instead. That exits 1 without a core dump. This is my reading, not a test.

Could you share two lines from stderr? First, the line starting FATAL ERROR. Second, the last rea_progress line. Those show the phase and the crash type.

Not run

  • Node 22.23.3 or Node 24 (Node 24.18.0 ran, on the ten-field loop and a 41k-node test app on main, not on bundles A or B. Node 22.23.3 did not run.)
  • A 7 GB, 4 vCPU box.
  • Your bundle, of course.
  • 6.2.0 on bundle A with a 1024 MB cap.
  • The exact crash point between 1024 and 1536 MB.
  • A 5120 MB cap. The 4144 MB default already passed.

Edit: Results for Node 24 are in my new comment. The other items in this list are still not run.

Thanks yemudo for the clear report and the numbers.

Test setup
  • Box: Nobara 44 Linux, x64
  • CPU: AMD Ryzen 9 5900X, 12 cores and 24 threads
  • RAM: 32 GB of DDR4-3600
  • Node: 22.23.1

🤖 Generated with Claude Code (Opus 5.5) 🦊

<!-- gh-comment-id:6092195093 --> @CassieFox23 commented on GitHub (Oct 10, 2026): **TL;DR:** I could not make 6.3.0 crash where 6.2.0 works. On two big public bundles, 6.3.0 used less memory. It also wrote smaller JSON. ### What I ran - Command: `rea analyze-javascript-application <asar> --json > out.json`. - I timed each run with `/usr/bin/time -v`. - Node's default heap here is 4144 MB. - I also capped the heap with `--max-old-space-size`. ### Bundle A: open source I used a copy of the [Beyond All Reason launcher](https://flathub.org/apps/info.beyondallreason.bar) (launcher code [MIT](https://github.com/beyond-all-reason/spring-launcher/blob/master/LICENSE); Flathub package [GPL-2.0-or-later](https://github.com/flathub/info.beyondallreason.bar/blob/60445fcc28ede165cd2977a869e29e25da01353b/info.beyondallreason.bar.metainfo.xml#L7)). Its Flathub id is `info.beyondallreason.bar`. It is version 1.2988.0.3. File: `app.asar`, 26,870,695 bytes. sha256: `595810df454127cd45783dca6fa0e139b70a22e435eace530816a35d83ad6f03` REA parsed 2,027 source files here. | version | heap cap | result | time | max RSS (GiB) | JSON | |---|---|---|---|---|---| | 6.2.0 | default | ok | 67 s | 2.0 | 563 MB | | 6.3.0 | default | ok | 52 s | 1.9 | 402 MB | | 6.3.0 | 2048 MB | ok | 53 s | 1.4 | 402 MB | | 6.3.0 | 1024 MB | ok | 53 s | 1.0 | 402 MB | Both versions found 11,666 findings. ### Bundle B: closed source This is a 27 MB proprietary Electron bundle from Flathub. It is closer to your shape. Most of it is minified code. REA parsed 38 source files. | version | heap cap | result | time | max RSS (GiB) | JSON | |---|---|---|---|---|---| | 6.2.0 | default | ok | 143 s | 2.7 | 890 MB | | 6.3.0 | default | ok | 139 s | 2.7 | 562 MB | | 6.2.0 | 2048 MB | ok | 132 s | 2.0 | 890 MB | | 6.3.0 | 2048 MB | ok | 132 s | 1.8 | 562 MB | | 6.2.0 | 1536 MB | ok | 130 s | 1.6 | 890 MB | | 6.3.0 | 1536 MB | ok | 132 s | 1.5 | 562 MB | | 6.2.0 | 1024 MB | abort, 134 | 30 s | 1.2 | none | | 6.3.0 | 1024 MB | abort, 134 | 67 s | 1.2 | none | Both versions found 1,464 findings. The four default-heap outputs parsed as JSON. Capped runs matched them in size, to the byte. At 1024 MB, both versions crashed the same way: ``` FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory ``` That is a plain heap limit. It is not "Invalid string length." Both crashed while still parsing. The JSON file was still empty. 6.2.0 died on file 1 of 38. 6.3.0 got to file 17 of 38. ### What this rules out - 6.2.0 wrote 890 MB of JSON fine. - 6.3.0 wrote 562 MB fine. That is past your 500 MB. - On these bundles, 6.3.0 needs no more heap than 6.2.0. - The new evidence contexts did not grow the output. 6.3.0 JSON was 29% to 37% smaller. ### What it does not rule out Your bundle has a different shape. It gave about 41.6k graph nodes. Both of mine hit the 100,000 node ceiling. So your crash may take another path. Your exit code 134 matches a V8 fatal error. A string limit would usually throw a `RangeError` instead. That exits 1 without a core dump. This is my reading, not a test. Could you share two lines from stderr? First, the line starting `FATAL ERROR`. Second, the last `rea_progress` line. Those show the phase and the crash type. ### Not run - Node 22.23.3 or ~~Node 24~~ (Node 24.18.0 ran, on the ten-field loop and a 41k-node test app on main, not on bundles A or B. Node 22.23.3 did not run.) - A 7 GB, 4 vCPU box. - Your bundle, of course. - 6.2.0 on bundle A with a 1024 MB cap. - The exact crash point between 1024 and 1536 MB. - A 5120 MB cap. The 4144 MB default already passed. Edit: Results for Node 24 are in [my new comment](https://github.com/morluto/rea/issues/1387#issuecomment-6101451272). The other items in this list are still not run. Thanks yemudo for the clear report and the numbers. <details> <summary>Test setup</summary> - Box: Nobara 44 Linux, x64 - CPU: AMD Ryzen 9 5900X, 12 cores and 24 threads - RAM: 32 GB of DDR4-3600 - Node: 22.23.1 </details> 🤖 Generated with Claude Code (Opus 5.5) 🦊
Author
Owner

@yemudo commented on GitHub (Oct 10, 2026):

Thanks for the thorough repro. Unfortunately our runner kept only the last 5 stderr lines (native stack frames), so we don't have the FATAL ERROR or last rea_progress line, and we've rolled back to 6.2.0 for now. Your exit-134 reading makes sense. When we next try a 6.3.x release we'll capture full stderr and post both lines here.

<!-- gh-comment-id:6092541073 --> @yemudo commented on GitHub (Oct 10, 2026): Thanks for the thorough repro. Unfortunately our runner kept only the last 5 stderr lines (native stack frames), so we don't have the `FATAL ERROR` or last `rea_progress` line, and we've rolled back to 6.2.0 for now. Your exit-134 reading makes sense. When we next try a 6.3.x release we'll capture full stderr and post both lines here.
Author
Owner

@CassieFox23 commented on GitHub (Oct 10, 2026):

TL;DR: Your crash may be #1401. It is a new slow path in 6.3.0. On one loop shape, 6.3.0 runs out of heap and exits 134. 6.2.0 does the same file in under 2 seconds. I can't tell yet if your bundle has that shape.

What I found

  • #1401 comes from #1292. That change is in 6.3.0, not in 6.2.0.
  • The trigger is a loop that moves one variable through many fields. Each extra field makes it about six times slower.
  • With ten fields, 6.3.0 ran out of heap every time. 6.2.0 never did. More heap only made it last longer, as in your 91 s and 253 s runs.
  • It died in the analysis step, before any JSON was written. So here it was not the string limit.
  • It also died in a test app with 41k nodes, close to your 41.6k.
  • My first test missed this loop shape. It is not in the Beyond All Reason launcher (launcher code MIT; Flathub package GPL-2.0-or-later).

Ten-field loop, through analyze-javascript-application --format json:

heap cap 6.2.0 6.3.0
1 GB ok, 1.7 s exit 134, 23 s
2 GB ok, 1.7 s exit 134, 45 s
default, 4 GB ok, 1.6 s exit 134, 89 s

The loop, from #1401:

let node = seed;
while (node) {
  if (flag === 0) node = node.m0;
  else if (flag === 1) node = node.m1;
  // and so on, up to m9
  use(node);
}

A cheap check

Next time, keep two stderr lines. Does the FATAL ERROR line say "heap out of memory"? Does the last rea_progress line say "Parsing and projecting" a file? If both, it died in this step, and #1401 fits. The real test is a build with the #1390 fix. I also have a small script that finds this loop shape, if it helps.

Not run

  • Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box.
  • Your default heap size. So I can't match your 91 s to a cap.
  • The #1390 fix on these tests. (Ran on main c12a532b9, which has #1390 and later commits. A build of 28595276f alone did not run.)
  • The loop scan on my closed-source test bundle.
  • Other code shapes. #1401 notes one more slow spot. This loop may not explain it.
  • More than one run per row. Other jobs ran on the box.

Edit: Results are in my new comment. I ran the ten-field loop and the 41k-node app on main c12a532b9. Neither crashed, on Node 24 or Node 22. The items in this list that are not struck are still not run. So is the timing comparison, which comes separately.

Thanks yemudo for the clear numbers. Your 91 s and 253 s pair was the best clue.

More runs

The #1401 loop with k fields, run straight through the analysis call. Times in seconds.

k 6.2.0, any cap 6.3.0, 1 GB 6.3.0, 2 GB 6.3.0, default
8 0.2 1.6 1.4 2.3
9 0.2 13 12 14
10 0.2 exit 134, 26 exit 134, 51 exit 134, 87
12 0.2 not run not run not run

Node 24.18.0 looks the same. At k = 10, 6.3.0 died after 78 s.

Through the CLI, with 41k nodes of padding:

app 6.2.0 6.3.0
no loop, default heap ok, 22 s ok, 15 s
9 fields, 1 GB ok, 23 s ok, 28 s
10 fields, 2 GB ok, 21 s exit 134, 40 s
10 fields, default ok, 22 s exit 134, 90 s
10 fields in the last file, 2 GB ok, 22 s exit 134, 51 s

Every 134 printed the same line. The last progress line named the file with the loop.

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

I scanned the launcher with a small parser script. It found 5,175 loops. None moves one variable through two or more fields.

Commands
( ulimit -c 0; export NODE_OPTIONS="--max-old-space-size=2048"
  timeout 180 /usr/bin/time -f "%e %M %x" nice -n 10 \
  rea analyze-javascript-application ./fake-app --format json > out.json )

For the straight runs, a small script imports analyzeJavaScriptSemantics from each install's dist folder.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB
  • Node: 22.23.1, 24.18.0
  • rea-agents: 6.2.0 a64851f2, 6.3.0 74cf9e14
  • #1292 commit: 355a2921

🤖 Generated with Claude Code (Opus 5.5) 🦊

<!-- gh-comment-id:6093390004 --> @CassieFox23 commented on GitHub (Oct 10, 2026): **TL;DR:** Your crash may be #1401. It is a new slow path in 6.3.0. On one loop shape, 6.3.0 runs out of heap and exits 134. 6.2.0 does the same file in under 2 seconds. I can't tell yet if your bundle has that shape. ### What I found - #1401 comes from #1292. That change is in 6.3.0, not in 6.2.0. - The trigger is a loop that moves one variable through many fields. Each extra field makes it about six times slower. - With ten fields, 6.3.0 ran out of heap every time. 6.2.0 never did. More heap only made it last longer, as in your 91 s and 253 s runs. - It died in the analysis step, before any JSON was written. So here it was not the string limit. - It also died in a test app with 41k nodes, close to your 41.6k. - My first test missed this loop shape. It is not in the [Beyond All Reason launcher](https://flathub.org/apps/info.beyondallreason.bar) (launcher code [MIT](https://github.com/beyond-all-reason/spring-launcher/blob/master/LICENSE); Flathub package [GPL-2.0-or-later](https://github.com/flathub/info.beyondallreason.bar/blob/60445fcc28ede165cd2977a869e29e25da01353b/info.beyondallreason.bar.metainfo.xml#L7)). Ten-field loop, through `analyze-javascript-application --format json`: | heap cap | 6.2.0 | 6.3.0 | |---|---|---| | 1 GB | ok, 1.7 s | exit 134, 23 s | | 2 GB | ok, 1.7 s | exit 134, 45 s | | default, 4 GB | ok, 1.6 s | exit 134, 89 s | The loop, from #1401: ```js let node = seed; while (node) { if (flag === 0) node = node.m0; else if (flag === 1) node = node.m1; // and so on, up to m9 use(node); } ``` ### A cheap check Next time, keep two stderr lines. Does the `FATAL ERROR` line say "heap out of memory"? Does the last `rea_progress` line say "Parsing and projecting" a file? If both, it died in this step, and #1401 fits. The real test is a build with the #1390 fix. I also have a small script that finds this loop shape, if it helps. ### Not run - Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box. - Your default heap size. So I can't match your 91 s to a cap. - ~~The #1390 fix on these tests.~~ (Ran on main c12a532b9, which has #1390 and later commits. A build of 28595276f alone did not run.) - The loop scan on my closed-source test bundle. - Other code shapes. #1401 notes one more slow spot. This loop may not explain it. - More than one run per row. Other jobs ran on the box. Edit: Results are in [my new comment](https://github.com/morluto/rea/issues/1387#issuecomment-6101451272). I ran the ten-field loop and the 41k-node app on main c12a532b9. Neither crashed, on Node 24 or Node 22. The items in this list that are not struck are still not run. So is the timing comparison, which comes separately. Thanks yemudo for the clear numbers. Your 91 s and 253 s pair was the best clue. <details> <summary>More runs</summary> The #1401 loop with k fields, run straight through the analysis call. Times in seconds. | k | 6.2.0, any cap | 6.3.0, 1 GB | 6.3.0, 2 GB | 6.3.0, default | |---|---|---|---|---| | 8 | 0.2 | 1.6 | 1.4 | 2.3 | | 9 | 0.2 | 13 | 12 | 14 | | 10 | 0.2 | exit 134, 26 | exit 134, 51 | exit 134, 87 | | 12 | 0.2 | not run | not run | not run | Node 24.18.0 looks the same. At k = 10, 6.3.0 died after 78 s. Through the CLI, with 41k nodes of padding: | app | 6.2.0 | 6.3.0 | |---|---|---| | no loop, default heap | ok, 22 s | ok, 15 s | | 9 fields, 1 GB | ok, 23 s | ok, 28 s | | 10 fields, 2 GB | ok, 21 s | exit 134, 40 s | | 10 fields, default | ok, 22 s | exit 134, 90 s | | 10 fields in the last file, 2 GB | ok, 22 s | exit 134, 51 s | Every 134 printed the same line. The last progress line named the file with the loop. ``` FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory ``` I scanned the launcher with a small parser script. It found 5,175 loops. None moves one variable through two or more fields. </details> <details> <summary>Commands</summary> ```sh ( ulimit -c 0; export NODE_OPTIONS="--max-old-space-size=2048" timeout 180 /usr/bin/time -f "%e %M %x" nice -n 10 \ rea analyze-javascript-application ./fake-app --format json > out.json ) ``` For the straight runs, a small script imports `analyzeJavaScriptSemantics` from each install's `dist` folder. </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.9 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB - Node: 22.23.1, 24.18.0 - rea-agents: 6.2.0 `a64851f2`, 6.3.0 `74cf9e14` - #1292 commit: `355a2921` </details> 🤖 Generated with Claude Code (Opus 5.5) 🦊
Author
Owner

@CassieFox23 commented on GitHub (Oct 10, 2026):

TL;DR: The ten-field loop no longer crashes on current main, c12a532b9 (#1622), at 1 GB, 2 GB or the default heap. Main has the #1390 fix plus 188 later commits, so I can't credit #1390 alone, and no GitHub release has #1390 yet. I have no stderr or bundle from you, so I can't say if your crash is this one.

What I found

  • I reran my earlier checks on main. The merge of #1390 is 28595276f (#1390). Its own text says it "does not establish a common cause for #1387".
  • Main also has 38 later commits that touch JavaScript analysis, such as b28c0337f (#1601). So a pass here is not proof that #1390 did it.
  • The ten-field loop exits 0 and writes its JSON on Node 24.18.0 and Node 22.23.1. So does a test app with 41,133 graph nodes, close to your 41.6k, that holds the same loop.
  • The control is 74cf9e140 (#1383), the 6.3.0 release. It still aborts with exit 134 on the ten-field loop at 1 GB. It prints the same heap error and stops at "Parsing and projecting main.js (1/2)", as in my first post.
  • The newest GitHub release is 6.3.0, from before #1390 merged. I checked with gh api repos/morluto/rea/releases/latest.

What it means for you: the loop shape I tested no longer crashes on main. I can't tell if your bundle has that shape, so your crash may be another cause.

Crash or not, per check (every row used analyze-javascript-application --format json):

check main, Node 24.18.0 main, Node 22.23.1 6.3.0, Node 24.18.0
ten fields, 1 GB heap ok ok exit 134
ten fields, 2 GB heap ok not run not run
ten fields, default heap ok ok not run
eight fields, 1 GB heap ok not run not run
nine fields, 1 GB heap ok not run not run
41k-node app, ten fields, 1 GB heap ok not run not run
41k-node app, ten fields, 2 GB heap ok not run not run
41k-node app, ten fields, default heap ok ok not run

The default heap is 4,288 MB on Node 24.18.0 and 4,144 MB on Node 22.23.1 here.

Still not run

  • The timing comparison. It is coming separately, on an idle machine.
  • Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box.
  • A build of 28595276f alone, to split #1390 from the later commits. It shows the fix is in 28595276f or the 15 commits before it.
  • Other code shapes, such as aliases, nested loops or the renderer slow spot that #1401 notes.
  • My closed-source test bundle and the Beyond All Reason launcher on main.
  • More than one run per row. The new runs are 5 per cell, on Node 24.18.0 only.

Edit: The timing results are in my new comment.

Thanks, yemudo, for offering to test the next 6.3.x release with full stderr. The FATAL ERROR line and the last rea_progress line will tell us the most.

Rough wall times and load

These are single runs, only rough, taken while another job ran on the box. They are not a timing comparison. Load average counts the threads that wanted a CPU, out of 24 here. It was read at the start of each run.

run exit wall (s) load average at start
main, Node 24.18.0, ten fields, 1 GB 0 1.1 2.10
main, Node 24.18.0, ten fields, 2 GB 0 1.1 2.10
main, Node 24.18.0, ten fields, default 0 1.1 2.10
main, Node 22.23.1, ten fields, 1 GB 0 1.4 2.10
main, Node 22.23.1, ten fields, default 0 1.4 2.41
main, Node 24.18.0, eight fields, 1 GB 0 1.2 2.41
main, Node 24.18.0, nine fields, 1 GB 0 1.2 2.41
main, Node 24.18.0, 41k nodes, 1 GB 0 10.0 2.41
main, Node 24.18.0, 41k nodes, 2 GB 0 9.9 3.28
main, Node 24.18.0, 41k nodes, default 0 10.3 3.63
main, Node 22.23.1, 41k nodes, default 0 12.2 3.85
6.3.0, Node 24.18.0, ten fields, 1 GB 134 17.7 4.62

The 6.3.0 control took 17.7 s here. My first post said 23 s. That run used Node 22.23.1 at a load average of 9.4. This one used Node 24.18.0 at 4.62.

The 41k-node runs wrote 68,354,599 bytes of JSON each. Every main run ended on the same last progress message. The 6.3.0 run ended on a different one, then died.

main, last rea_progress message:  Application graph and Electron boundaries reconstructed
6.3.0, last rea_progress message: Parsing and projecting main.js (1/2)
6.3.0, FATAL line:
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Commands

Main was built from the tree at c12a532b9 with npm ci and npm run build, on Node 24.18.0. The test apps come from the same generator I used last time: genapp.mjs <dir> 10 0 0 for the ten-field loop and genapp.mjs <dir> 10 380 3 for the 41k-node app.

( ulimit -c 0; env -i HOME=/tmp/p1387a/home PATH=<node bin>:/usr/bin:/bin \
  TMPDIR=/tmp/p1387a/tmp NODE_OPTIONS=--max-old-space-size=1024 \
  nice -n 19 timeout 600 node scripts/rea.mjs \
  analyze-javascript-application ./fake-app --format json > out.json )

For "default heap", I left NODE_OPTIONS unset.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 22.23.1, 24.18.0
  • Main: c12a532b9 (#1622), after 28595276f (#1390)
  • Other: 6.3.0 for the control, 74cf9e140 (#1383)

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6101451272 --> @CassieFox23 commented on GitHub (Oct 10, 2026): **TL;DR:** The ten-field loop no longer crashes on current main, c12a532b9 (#1622), at 1 GB, 2 GB or the default heap. Main has the #1390 fix plus 188 later commits, so I can't credit #1390 alone, and no GitHub release has #1390 yet. I have no stderr or bundle from you, so I can't say if your crash is this one. ### What I found - I reran my earlier checks on main. The merge of #1390 is 28595276f (#1390). Its own text says it "does not establish a common cause for #1387". - Main also has 38 later commits that touch JavaScript analysis, such as b28c0337f (#1601). So a pass here is not proof that #1390 did it. - The ten-field loop exits 0 and writes its JSON on Node 24.18.0 and Node 22.23.1. So does a test app with 41,133 graph nodes, close to your 41.6k, that holds the same loop. - The control is 74cf9e140 (#1383), the 6.3.0 release. It still aborts with exit 134 on the ten-field loop at 1 GB. It prints the same heap error and stops at "Parsing and projecting main.js (1/2)", as in my first post. - The newest GitHub release is 6.3.0, from before #1390 merged. I checked with `gh api repos/morluto/rea/releases/latest`. What it means for you: the loop shape I tested no longer crashes on main. I can't tell if your bundle has that shape, so your crash may be another cause. Crash or not, per check (every row used `analyze-javascript-application --format json`): | check | main, Node 24.18.0 | main, Node 22.23.1 | 6.3.0, Node 24.18.0 | |---|---|---|---| | ten fields, 1 GB heap | ok | ok | exit 134 | | ten fields, 2 GB heap | ok | not run | not run | | ten fields, default heap | ok | ok | not run | | eight fields, 1 GB heap | ok | not run | not run | | nine fields, 1 GB heap | ok | not run | not run | | 41k-node app, ten fields, 1 GB heap | ok | not run | not run | | 41k-node app, ten fields, 2 GB heap | ok | not run | not run | | 41k-node app, ten fields, default heap | ok | ok | not run | The default heap is 4,288 MB on Node 24.18.0 and 4,144 MB on Node 22.23.1 here. ### Still not run - ~~The timing comparison. It is coming separately, on an idle machine.~~ - Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box. - ~~A build of 28595276f alone, to split #1390 from the later commits.~~ It shows the fix is in 28595276f or the 15 commits before it. - Other code shapes, such as aliases, nested loops or the renderer slow spot that #1401 notes. - My closed-source test bundle and the Beyond All Reason launcher on main. - ~~More than one run per row.~~ The new runs are 5 per cell, on Node 24.18.0 only. Edit: The timing results are in [my new comment](https://github.com/morluto/rea/issues/1387#issuecomment-6102189605). Thanks, yemudo, for offering to test the next 6.3.x release with full stderr. The `FATAL ERROR` line and the last `rea_progress` line will tell us the most. <details> <summary>Rough wall times and load</summary> These are single runs, only rough, taken while another job ran on the box. They are not a timing comparison. Load average counts the threads that wanted a CPU, out of 24 here. It was read at the start of each run. | run | exit | wall (s) | load average at start | |---|---|---|---| | main, Node 24.18.0, ten fields, 1 GB | 0 | 1.1 | 2.10 | | main, Node 24.18.0, ten fields, 2 GB | 0 | 1.1 | 2.10 | | main, Node 24.18.0, ten fields, default | 0 | 1.1 | 2.10 | | main, Node 22.23.1, ten fields, 1 GB | 0 | 1.4 | 2.10 | | main, Node 22.23.1, ten fields, default | 0 | 1.4 | 2.41 | | main, Node 24.18.0, eight fields, 1 GB | 0 | 1.2 | 2.41 | | main, Node 24.18.0, nine fields, 1 GB | 0 | 1.2 | 2.41 | | main, Node 24.18.0, 41k nodes, 1 GB | 0 | 10.0 | 2.41 | | main, Node 24.18.0, 41k nodes, 2 GB | 0 | 9.9 | 3.28 | | main, Node 24.18.0, 41k nodes, default | 0 | 10.3 | 3.63 | | main, Node 22.23.1, 41k nodes, default | 0 | 12.2 | 3.85 | | 6.3.0, Node 24.18.0, ten fields, 1 GB | 134 | 17.7 | 4.62 | The 6.3.0 control took 17.7 s here. My first post said 23 s. That run used Node 22.23.1 at a load average of 9.4. This one used Node 24.18.0 at 4.62. The 41k-node runs wrote 68,354,599 bytes of JSON each. Every main run ended on the same last progress message. The 6.3.0 run ended on a different one, then died. ``` main, last rea_progress message: Application graph and Electron boundaries reconstructed 6.3.0, last rea_progress message: Parsing and projecting main.js (1/2) 6.3.0, FATAL line: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory ``` </details> <details> <summary>Commands</summary> Main was built from the tree at c12a532b9 with `npm ci` and `npm run build`, on Node 24.18.0. The test apps come from the same generator I used last time: `genapp.mjs <dir> 10 0 0` for the ten-field loop and `genapp.mjs <dir> 10 380 3` for the 41k-node app. ```sh ( ulimit -c 0; env -i HOME=/tmp/p1387a/home PATH=<node bin>:/usr/bin:/bin \ TMPDIR=/tmp/p1387a/tmp NODE_OPTIONS=--max-old-space-size=1024 \ nice -n 19 timeout 600 node scripts/rea.mjs \ analyze-javascript-application ./fake-app --format json > out.json ) ``` For "default heap", I left `NODE_OPTIONS` unset. </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.9 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB DDR4-3600 - Node: 22.23.1, 24.18.0 - Main: c12a532b9 (#1622), after 28595276f (#1390) - Other: 6.3.0 for the control, 74cf9e140 (#1383) </details> 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Author
Owner

@CassieFox23 commented on GitHub (Oct 10, 2026):

TL;DR: On an idle machine, 6.3.0 still crashes on the ten-field loop and the 41k-node app, after about 64 seconds, even with a 4 GB heap. Commit 28595276f (#1390) and current main do not crash, and main is the fastest build. I did not test your bundle, so I still can't say your crash is this one.

What I found

Median wall time in seconds over five runs per cell, default heap. The command was analyze-javascript-application <dir> --format json, on Node 24.18.0.

Case 6.2.0 6.3.0 28595276f (#1390) main bbd01923e
6 fields 1.2 1.2 1.2 1.0
7 fields 1.2 1.3 1.2 1.0
8 fields 1.2 2.1 1.2 1.0
9 fields 1.2 10.6 1.2 1.0
10 fields 1.2 exit 134 at 63.8 s 1.2 1.0
41k-node app 16.5 exit 134 at 63.7 s 11.1 9.0
  • A 4 GB heap (--max-old-space-size=4096) gave the same medians within 0.2 s. 6.3.0 crashed in 20 of 20 runs on the two crash rows, at both heap sizes, and the crash times moved by less than 1 s. Each run ended with the out-of-memory FATAL ERROR line after peaking near 4.1 GiB. My last comment showed 17.7 s at a 1 GB heap. A bigger heap lets the crash come later.
  • 6.3.0 gets slower with each added field: 1.2 s at 6 fields, 2.1 s at 8, 10.6 s at 9. The other three builds stay flat.
  • All 180 runs of 6.2.0, 28595276f and main exited 0. Main is fastest on the 41k-node app: 9.0 s, against 11.1 s and 16.5 s.
  • 28595276f is 16 commits after 6.3.0, and #1390 is the last one. It does not crash, so the fix is in those 16 commits, not in main's later ones. It does not split #1390 from the 15 commits before it.
  • Main moved since my last comment, from c12a532b9 (#1622) to bbd01923e, 14 commits later. That comment gave main about 10 s on the 41k-node app, read while another job ran. The 9.0 s here is from an idle machine.
  • Load average is a count of threads that want a processor, averaged over one minute. This machine has 24 threads, so 24 would mean full. It read 0.44 to 1.98 at the start of each run. Eleven runs waited 15 to 45 s for it to fall under 2.0.

Still not run

  • 6.4.0. Release PR #1399 is still open and has no tag.
  • Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box.
  • A build of the parent of 28595276f, to split #1390 from the 15 earlier commits.
  • Timings on Node 22. Every run here used Node 24.18.0.
  • Other code shapes, such as aliases or nested loops.
All cells, median (min to max), both heap sizes

Default heap:

Case 6.2.0 6.3.0 28595276f (#1390) main bbd01923e
6 fields 1.2 (1.18 to 1.24) 1.2 (1.21 to 1.23) 1.2 (1.16 to 1.18) 1.0 (0.98 to 0.99)
7 fields 1.2 (1.18 to 1.22) 1.3 (1.32 to 1.38) 1.2 (1.19 to 1.21) 1.0 (0.99 to 1.05)
8 fields 1.2 (1.18 to 1.20) 2.1 (2.11 to 2.17) 1.2 (1.17 to 1.19) 1.0 (0.97 to 1.03)
9 fields 1.2 (1.20 to 1.22) 10.6 (10.40 to 10.85) 1.2 (1.19 to 1.23) 1.0 (1.00 to 1.11)
10 fields 1.2 (1.19 to 1.26) exit 134 at 63.8 s (62.58 to 65.16) 1.2 (1.17 to 1.21) 1.0 (0.99 to 1.07)
41k-node app 16.5 (16.24 to 16.59) exit 134 at 63.7 s (63.24 to 64.58) 11.1 (10.91 to 11.37) 9.0 (8.88 to 9.11)

4 GB heap:

Case 6.2.0 6.3.0 28595276f (#1390) main bbd01923e
6 fields 1.2 (1.18 to 1.24) 1.2 (1.23 to 1.26) 1.2 (1.17 to 1.21) 1.0 (0.98 to 1.05)
7 fields 1.2 (1.18 to 1.24) 1.3 (1.33 to 1.42) 1.2 (1.16 to 1.21) 1.0 (0.99 to 1.03)
8 fields 1.2 (1.16 to 1.24) 2.2 (2.13 to 2.18) 1.2 (1.16 to 1.22) 1.0 (0.99 to 1.00)
9 fields 1.2 (1.17 to 1.21) 10.4 (10.35 to 10.75) 1.2 (1.17 to 1.21) 1.0 (0.99 to 1.05)
10 fields 1.2 (1.19 to 1.23) exit 134 at 63.0 s (61.82 to 65.01) 1.2 (1.17 to 1.23) 1.0 (0.98 to 1.06)
41k-node app 16.4 (16.15 to 16.60) exit 134 at 64.3 s (62.83 to 65.36) 11.2 (10.95 to 11.23) 9.1 (8.93 to 9.14)

Peak memory on the 41k-node app, default heap, median: 6.2.0 767 MiB, 28595276f 677 MiB, main 635 MiB, 6.3.0 4,240 MiB (it crashed).

Commands

Test apps come from the same generator as before: genapp.mjs <dir> <k> 0 0 for the k-field loops and genapp.mjs <dir> 10 380 3 for the 41k-node app. I built main and 28595276f with npm ci && npm run build on Node 24.18.0.

Runs went round-robin across the four builds, one process at a time, with no nice. The script waited, up to 5 minutes, while the one-minute load average was above 2.0.

( ulimit -c 0; /usr/bin/time -f "%e %M %x" env -i HOME=/tmp/p1387c/home \
  PATH=$HOME/.local/opt/node24/bin:/usr/bin:/bin TMPDIR=/tmp/p1387c/tmp \
  [NODE_OPTIONS=--max-old-space-size=4096] timeout 600 node <build>/scripts/rea.mjs \
  analyze-javascript-application <dir> --format json > /dev/null )

For the default heap, NODE_OPTIONS was unset.

I meant to skip 6.3.0 reps after two crashes. My script read %x as 0 for a killed process, so it never saw the crashes and all five reps ran. I took the real exit code, 134, from the line that time prints for a killed process ("Command terminated by signal 6").

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 24.18.0
  • Main: bbd01923e. Also built: 28595276f (#1390)
  • Other: 6.2.0 is a64851f25 (#1305) and 6.3.0 is 74cf9e140 (#1383). I installed the released packages and did not compare their files to those commits.

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6102189605 --> @CassieFox23 commented on GitHub (Oct 10, 2026): **TL;DR:** On an idle machine, 6.3.0 still crashes on the ten-field loop and the 41k-node app, after about 64 seconds, even with a 4 GB heap. Commit 28595276f (#1390) and current main do not crash, and main is the fastest build. I did not test your bundle, so I still can't say your crash is this one. ### What I found Median wall time in seconds over five runs per cell, default heap. The command was `analyze-javascript-application <dir> --format json`, on Node 24.18.0. | Case | 6.2.0 | 6.3.0 | 28595276f (#1390) | main bbd01923e | |---|---|---|---|---| | 6 fields | 1.2 | 1.2 | 1.2 | 1.0 | | 7 fields | 1.2 | 1.3 | 1.2 | 1.0 | | 8 fields | 1.2 | 2.1 | 1.2 | 1.0 | | 9 fields | 1.2 | 10.6 | 1.2 | 1.0 | | 10 fields | 1.2 | exit 134 at 63.8 s | 1.2 | 1.0 | | 41k-node app | 16.5 | exit 134 at 63.7 s | 11.1 | 9.0 | - A 4 GB heap (`--max-old-space-size=4096`) gave the same medians within 0.2 s. 6.3.0 crashed in 20 of 20 runs on the two crash rows, at both heap sizes, and the crash times moved by less than 1 s. Each run ended with the out-of-memory `FATAL ERROR` line after peaking near 4.1 GiB. My last comment showed 17.7 s at a 1 GB heap. A bigger heap lets the crash come later. - 6.3.0 gets slower with each added field: 1.2 s at 6 fields, 2.1 s at 8, 10.6 s at 9. The other three builds stay flat. - All 180 runs of 6.2.0, 28595276f and main exited 0. Main is fastest on the 41k-node app: 9.0 s, against 11.1 s and 16.5 s. - 28595276f is 16 commits after 6.3.0, and #1390 is the last one. It does not crash, so the fix is in those 16 commits, not in main's later ones. It does not split #1390 from the 15 commits before it. - Main moved since my last comment, from c12a532b9 (#1622) to bbd01923e, 14 commits later. That comment gave main about 10 s on the 41k-node app, read while another job ran. The 9.0 s here is from an idle machine. - Load average is a count of threads that want a processor, averaged over one minute. This machine has 24 threads, so 24 would mean full. It read 0.44 to 1.98 at the start of each run. Eleven runs waited 15 to 45 s for it to fall under 2.0. ### Still not run - 6.4.0. Release PR #1399 is still open and has no tag. - Your bundle, Node 22.23.3, or a 7 GB, 4 vCPU box. - A build of the parent of 28595276f, to split #1390 from the 15 earlier commits. - Timings on Node 22. Every run here used Node 24.18.0. - Other code shapes, such as aliases or nested loops. <details> <summary>All cells, median (min to max), both heap sizes</summary> Default heap: | Case | 6.2.0 | 6.3.0 | 28595276f (#1390) | main bbd01923e | |---|---|---|---|---| | 6 fields | 1.2 (1.18 to 1.24) | 1.2 (1.21 to 1.23) | 1.2 (1.16 to 1.18) | 1.0 (0.98 to 0.99) | | 7 fields | 1.2 (1.18 to 1.22) | 1.3 (1.32 to 1.38) | 1.2 (1.19 to 1.21) | 1.0 (0.99 to 1.05) | | 8 fields | 1.2 (1.18 to 1.20) | 2.1 (2.11 to 2.17) | 1.2 (1.17 to 1.19) | 1.0 (0.97 to 1.03) | | 9 fields | 1.2 (1.20 to 1.22) | 10.6 (10.40 to 10.85) | 1.2 (1.19 to 1.23) | 1.0 (1.00 to 1.11) | | 10 fields | 1.2 (1.19 to 1.26) | exit 134 at 63.8 s (62.58 to 65.16) | 1.2 (1.17 to 1.21) | 1.0 (0.99 to 1.07) | | 41k-node app | 16.5 (16.24 to 16.59) | exit 134 at 63.7 s (63.24 to 64.58) | 11.1 (10.91 to 11.37) | 9.0 (8.88 to 9.11) | 4 GB heap: | Case | 6.2.0 | 6.3.0 | 28595276f (#1390) | main bbd01923e | |---|---|---|---|---| | 6 fields | 1.2 (1.18 to 1.24) | 1.2 (1.23 to 1.26) | 1.2 (1.17 to 1.21) | 1.0 (0.98 to 1.05) | | 7 fields | 1.2 (1.18 to 1.24) | 1.3 (1.33 to 1.42) | 1.2 (1.16 to 1.21) | 1.0 (0.99 to 1.03) | | 8 fields | 1.2 (1.16 to 1.24) | 2.2 (2.13 to 2.18) | 1.2 (1.16 to 1.22) | 1.0 (0.99 to 1.00) | | 9 fields | 1.2 (1.17 to 1.21) | 10.4 (10.35 to 10.75) | 1.2 (1.17 to 1.21) | 1.0 (0.99 to 1.05) | | 10 fields | 1.2 (1.19 to 1.23) | exit 134 at 63.0 s (61.82 to 65.01) | 1.2 (1.17 to 1.23) | 1.0 (0.98 to 1.06) | | 41k-node app | 16.4 (16.15 to 16.60) | exit 134 at 64.3 s (62.83 to 65.36) | 11.2 (10.95 to 11.23) | 9.1 (8.93 to 9.14) | Peak memory on the 41k-node app, default heap, median: 6.2.0 767 MiB, 28595276f 677 MiB, main 635 MiB, 6.3.0 4,240 MiB (it crashed). </details> <details> <summary>Commands</summary> Test apps come from the same generator as before: `genapp.mjs <dir> <k> 0 0` for the k-field loops and `genapp.mjs <dir> 10 380 3` for the 41k-node app. I built main and 28595276f with `npm ci && npm run build` on Node 24.18.0. Runs went round-robin across the four builds, one process at a time, with no `nice`. The script waited, up to 5 minutes, while the one-minute load average was above 2.0. ```sh ( ulimit -c 0; /usr/bin/time -f "%e %M %x" env -i HOME=/tmp/p1387c/home \ PATH=$HOME/.local/opt/node24/bin:/usr/bin:/bin TMPDIR=/tmp/p1387c/tmp \ [NODE_OPTIONS=--max-old-space-size=4096] timeout 600 node <build>/scripts/rea.mjs \ analyze-javascript-application <dir> --format json > /dev/null ) ``` For the default heap, `NODE_OPTIONS` was unset. I meant to skip 6.3.0 reps after two crashes. My script read `%x` as 0 for a killed process, so it never saw the crashes and all five reps ran. I took the real exit code, 134, from the line that `time` prints for a killed process ("Command terminated by signal 6"). </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.9 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB DDR4-3600 - Node: 24.18.0 - Main: bbd01923e. Also built: 28595276f (#1390) - Other: 6.2.0 is a64851f25 (#1305) and 6.3.0 is 74cf9e140 (#1383). I installed the released packages and did not compare their files to those commits. </details> 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
Author
Owner

@yemudo commented on GitHub (Oct 11, 2026):

Confirmed fixed for us on 6.4.0: our bundle (~41k-node app) now completes in ~65 s (6.2.0 took ~230 s), same findings, and the JSON is ~7 MB instead of ~500 MB. Thanks for the quick turnaround. Closing from our side.

<!-- gh-comment-id:6110457127 --> @yemudo commented on GitHub (Oct 11, 2026): Confirmed fixed for us on 6.4.0: our bundle (~41k-node app) now completes in ~65 s (6.2.0 took ~230 s), same findings, and the JSON is ~7 MB instead of ~500 MB. Thanks for the quick turnaround. Closing from our side.
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Closing as fixed based on your confirmation on 6.4.0: the original ~41k-node application now completes in ~65 seconds with the same findings and ~7 MB JSON. Thank you for verifying the original private workload.

The literal-retention and member-mutation fixes are on main (#1407, #1390). Separate remaining performance work is tracked independently; this closure is for the reported 6.3.0 abort.

<!-- gh-comment-id:6161743947 --> @morluto commented on GitHub (Oct 14, 2026): Closing as fixed based on your confirmation on 6.4.0: the original ~41k-node application now completes in ~65 seconds with the same findings and ~7 MB JSON. Thank you for verifying the original private workload. The literal-retention and member-mutation fixes are on main (#1407, #1390). Separate remaining performance work is tracked independently; this closure is for the reported 6.3.0 abort.
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#481
No description provided.