Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RangeError: Maximum call stack size exceeded when require/load very big json file #135

Closed
dongyuwei opened this issue Oct 16, 2019 · 83 comments
Assignees
Labels
bug Something isn't working

Comments

@dongyuwei
Copy link

https://github.com/dongyuwei/tiny_english_dictionary/blob/hermes/trie-service.js#L2
const words = require('./words_with_frequency_and_translation_and_ipa.json'); will throw error:

RangeError: Maximum call stack size exceeded, js engine:hermes

The json file size is 21M, while its data structure is very simple.

Steps to reproduce the bug:

  1. yarn start
  2. start an android emulator
  3. yarn android

Tested with yarn 1.17.3 and nodejs 12.12.0 on Mac 10.14.5.

@dongyuwei
Copy link
Author

Exception call stack:
image

@tmikov tmikov self-assigned this Oct 16, 2019
@tmikov
Copy link
Contributor

tmikov commented Oct 16, 2019

Thanks for reporting this. It seems like we have a problem with compiling large JSON files. We are looking into it and will post an update here.

Meanwhile, as a workaround can you parse as a string with JSON.parse()?

@tmikov tmikov added the bug Something isn't working label Oct 16, 2019
@dongyuwei
Copy link
Author

Thanks for your reply, I'll try and let you know today. On road now.

@dongyuwei
Copy link
Author

dongyuwei commented Oct 17, 2019

I just verified JSON.parse works. See https://github.com/dongyuwei/tiny_english_dictionary/blob/hermes/trie-service.js#L18, I use react-native-local-resource to load the stringified json txt, then use JSON.parse to decode it.

@sofiageo
Copy link

In my case I have a 14MB file which builds fine in debug mode but Hermes hangs when trying to optimize. I had to turn off hermes optimizations for the build to complete. Let me know if I can help to somehow debug this for you.

@dongyuwei
Copy link
Author

There is a unit test to parse json: /unittests/Parser/JSONParserTest.cpp, but I don't know how to run the test.

@tmikov can you modify and run the test with my big json file words_with_frequency_and_translation_and_ipa.json? From the call stack of the exception, can't see any direct relationship with hermes.

@tmikov
Copy link
Contributor

tmikov commented Jan 30, 2020

We investigated this when it was reported and we believe we understand the bug. We haven't forgotten it, and we have been workin to address it, but progress has been slow because the fix is non-trivial. There are two separate problems:

  • Without optimizations, the generated code for large nested object literals consume inordinate amount of register stack, causing the stack overflow exception.
  • With optimizations, the stack usage is much better, but one particular optimization pass exhibits degenerate behavior on the same kind of object literals.

The workaround is to use JSON.parse() with a string or a resource.

I will look into escalating this, so the fix can land sooner.

@aprilmintacpineda
Copy link

aprilmintacpineda commented Mar 17, 2021

This happened to me when I upgraded my react-native https://react-native-community.github.io/upgrade-helper/?from=0.63.4&to=0.64.0

 WARN  Require cycle: node_modules/core-js/internals/microtask.js -> node_modules/core-js/internals/microtask.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Blob.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Fetch.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 ERROR  RangeError: Maximum call stack size exceeded, js engine: hermes
 LOG  Running "ny_app" with {"rootTag":1}
 ERROR  Invariant Violation: "ny_app" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

Happens both on android and ios.

@melroyd16
Copy link

This happened to me when I upgraded my react-native https://react-native-community.github.io/upgrade-helper/?from=0.63.4&to=0.64.0

 WARN  Require cycle: node_modules/core-js/internals/microtask.js -> node_modules/core-js/internals/microtask.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Blob.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Fetch.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 ERROR  RangeError: Maximum call stack size exceeded, js engine: hermes
 LOG  Running "ny_app" with {"rootTag":1}
 ERROR  Invariant Violation: "ny_app" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

Happens both on android and ios.

Setting inlineRequires: false in metro.config.js fixed it for me

@chensu-chime
Copy link

We investigated this when it was reported and we believe we understand the bug. We haven't forgotten it, and we have been workin to address it, but progress has been slow because the fix is non-trivial. There are two separate problems:

  • Without optimizations, the generated code for large nested object literals consume inordinate amount of register stack, causing the stack overflow exception.
  • With optimizations, the stack usage is much better, but one particular optimization pass exhibits degenerate behavior on the same kind of object literals.

The workaround is to use JSON.parse() with a string or a resource.

I will look into escalating this, so the fix can land sooner.

Is there any update on the fix?

@marcibk
Copy link

marcibk commented Jun 9, 2021

I have a 13mb JSON file which should be loaded locally. I tried basically everything, but it always gives me this error... Can someone help me? Any working work-around for this? Or a fix somehow? Thank you... @tmikov

@tmikov
Copy link
Contributor

tmikov commented Jun 9, 2021

@traingethermarc the workaround is to use JSON.parse().

@aprilmintacpineda
Copy link

aprilmintacpineda commented Jun 21, 2021

This happened to me when I upgraded my react-native https://react-native-community.github.io/upgrade-helper/?from=0.63.4&to=0.64.0

 WARN  Require cycle: node_modules/core-js/internals/microtask.js -> node_modules/core-js/internals/microtask.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Blob.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Fetch.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 ERROR  RangeError: Maximum call stack size exceeded, js engine: hermes
 LOG  Running "ny_app" with {"rootTag":1}
 ERROR  Invariant Violation: "ny_app" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

Happens both on android and ios.

This is happening to one of the apps I'm working on (1) BUT it does not import any big JSON files. On the other hand, the other app I'm working on (2) doesn't experience this, could it be related to auth0? because my former uses auth0 but the latter does not.

@dongdyang
Copy link

it has been two years. And this issue still occurs for the latest 0.9 version hermes.

@tmikov tmikov assigned neildhar and unassigned tmikov Nov 4, 2021
@cristianoccazinsp
Copy link

What's hermes' max call stack size? I'm getting max call stack size errors for recursive functions as deep as just 60 calls.

@tmikov
Copy link
Contributor

tmikov commented Jan 31, 2022

@cristianoccazinsp For JS to JS recursion, the maximum depth is dynamic and depends on the size of every frame and the size of the register stack, which is configurable. In the default configuration, the following code performs 55,000 recursive invocations without optimization, and 87,000 with optimization:

let level = 0;
function foo() {
    ++level;
    foo();
}

try {
    foo();
} catch (e) {
    print("Level", level);
    print(e);
}

Hermes has a separate fixed stack limit for native calls, which is set to 128. So, if you change the recursive invocation from foo() to foo.call(), it throws after 128 recursive calls.

@cristianoccazinsp
Copy link

Native stack errors is just the crash I'm facing right now. Sigh, 128 is so small! This used to work fine with JSC and now blows up with Hermes. Sadly, the code is for an HTML parsing library that relies on recursion so 128 will definitely cause issues as HTMLs could be huge.

@tmikov
Copy link
Contributor

tmikov commented Jan 31, 2022

Keep in mind that the low number is only for cases involving recursion through a native function. The number is deliberately very conservative to guarantee that a native stack overflow will never occur. It is even lower in some build modes, because the native stack (which is not controlled by Hermes) can and does overflow quite easily.

The limit can be increased by changing this line:

There is some work in progress for obtaining and checking the actual stack size, which on most cases will result in significantly increased depth, but no promises when it will land.

@cristianoccazinsp
Copy link

I'm not entirely sure why I get native stack depth errors even though the recursion happens entirely in JS. Does calls to .map and "native" methods count towards the native stack?

@tmikov
Copy link
Contributor

tmikov commented Jan 31, 2022

Yes. Most "standard library" functions are implemented in C++ and unfortunately count as native.

The best way to address this is to check whether the native stack is actually overflowing, instead of setting artificial hard limits. I will see if I can finish that work.

@cristianoccazinsp
Copy link

@tmikov I replaced all .map calls with simple for a in b loops and the recursion errors went away (even though I'm still using recursive calls). Funny thing is, the moment the screen gets unmounted, another native stack depth error occurs from a totally different place (react-navigation stack and animations).

stdavis added a commit to agrc/roadkill-mobile that referenced this issue Feb 4, 2022
@aike19115
Copy link

With this comment I want to raise awareness of this issue and show that there is a high demand for it to be fixed. (Even though there is a workaround to import as string and then use JSON.parse)

facebook-github-bot pushed a commit that referenced this issue Apr 7, 2023
Summary:
Original Author: 4092035+mikeduminy@users.noreply.github.com
Original Git: 1b759f4

As discussed in #135, the default stack size doesn't work for all use cases. In particular, when very large and complex bundles are loaded in dev mode.

This PR bumps the default stack size from `64*1024` (512kB) to `128*1024` (1MB). As suggested by tmikov in this comment - #135 (comment).

Pull Request resolved: #923

Original Reviewed By: tmikov

Original Revision: D43630032

Reviewed By: tmikov

Differential Revision: D44770995

fbshipit-source-id: 400221033e4c1e079535342331c99561f141b830
lunaleaps pushed a commit to lunaleaps/hermes that referenced this issue Apr 12, 2023
Summary:
As discussed in facebook#135, the default stack size doesn't work for all use cases. In particular, when very large and complex bundles are loaded in dev mode.

This PR bumps the default stack size from `64*1024` (512kB) to `128*1024` (1MB). As suggested by tmikov in this comment - facebook#135 (comment).

Pull Request resolved: facebook#923

Reviewed By: tmikov

Differential Revision: D43630032

Pulled By: neildhar

fbshipit-source-id: 5f8cff91a5f01b6507870c61efa1ce507de67940
@Waltari10
Copy link

Waltari10 commented Apr 13, 2023

Hi, I'm trying to upgrade to Hermes in our RN app and I'm getting the ERROR RangeError: Maximum call stack size exceeded (native stack depth), js engine: hermes. No big JSON files. I guess this is just Javascript code accessing the bridge too often?

Is the default max stack size going to be increased in RN 0.72?

@mikeduminy
Copy link
Contributor

@Waltari10 the fix has landed in RN 0.71.4 and will likely be cherry picked into RN 0.70.9. It will also be in RN 0.72.

@Waltari10
Copy link

@Waltari10 the fix has landed in RN 0.71.4 and will likely be cherry picked into RN 0.70.9. It will also be in RN 0.72.

Thanks for the info! Curiously we are already using 0.71.4 in our app though and still getting the error 🤔

@mikeduminy
Copy link
Contributor

mikeduminy commented Apr 13, 2023

Disclaimer: I'm not an expert.

Oh I just noticed that your error message includes (native stack depth) which is a different error. The one that was fixed in RN 0.71.4 was a JS Register max stack size error, but yours is about the native stack size (which is much lower). You can find more information in this comment.

No big JSON files. I guess this is just Javascript code accessing the bridge too often?

Since this is a native stack issue I guess that neither JSON nor frequent bridge access is the problem. What does your stack trace look like?

Have a look at the stack trace in this comment. Notice the multiple lines that say: at apply (native). This indicates that a native call is being made. Note: this is not a react native call, but rather what the runtime considers a native call, which would be things like Function.prototype.call, Function.prototype.apply, or (maybe) Array.prototype.map.

Maybe the transpilation step of your builds is adding too many of these native calls to functions that are called in a deeply-nested way. You could see if any changes to your babel settings could fix it. Or perhaps you need to hunt down what changed in your codebase from when it was working.

@aurangs7
Copy link

This happened to me when I upgraded my react-native https://react-native-community.github.io/upgrade-helper/?from=0.63.4&to=0.64.0

 WARN  Require cycle: node_modules/core-js/internals/microtask.js -> node_modules/core-js/internals/microtask.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Blob.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/XMLHttpRequest.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 WARN  Require cycle: node_modules/rn-fetch-blob/index.js -> node_modules/rn-fetch-blob/polyfill/index.js -> node_modules/rn-fetch-blob/polyfill/Fetch.js -> node_modules/rn-fetch-blob/index.js

Require cycles are allowed, but can result in uninitialized values. Consider refactoring to remove the need for a cycle.
 ERROR  RangeError: Maximum call stack size exceeded, js engine: hermes
 LOG  Running "ny_app" with {"rootTag":1}
 ERROR  Invariant Violation: "ny_app" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes

Happens both on android and ios.

Setting inlineRequires: false in metro.config.js fixed it for me

I am also getting this error in Android (iOS working fine), this happened suddenly.
It was working fine till yesterday.

inlineRequires: false => is already added in file

Any solution?

Screenshot_1683805028

@antondalgren
Copy link

antondalgren commented May 12, 2023

Hi,

Been struck by this issue too when migrating from RN 0.69 to 0.70.9 and enabling hermes in the process.

Would this limitation of the call stack on Function.call() also be applicable to the usage of Array.prototype.reduce? As the implementation of the reduce function is calling the Callable::executeCall4 which in turn uses ScopedNativeCallFrame.

auto callRes = Callable::executeCall4(

CallResult<PseudoHandle<>> Callable::executeCall4(

We are essentially using AJV for JSON validation, and that library is using recursion within the call and reduce method.

Also noted that Function.apply() is also limited by this call stack.

ScopedNativeCallFrame newFrame{runtime, 0, *func, false, args.getArg(0)};

@tmikov
Copy link
Contributor

tmikov commented May 12, 2023

FWIW, this has been addressed in the next version of Hermes, but unfortunately we do not yet have a timeline for release or for backporting.

@alvarovillafane
Copy link

Hi @tmikov,

Would you mind helping me to clarify some things I'm a bit confused about?

I just got hit by the RangeError: Maximum call stack size exceeded (native stack depth) issue while working on my app.

This seems to be different from the original issue of this thread that was fixed in #135 (comment)
I'm correct? fixing this issue will cover both cases or we should have another issue to track the "native stack depth" one?

In any case, my issue is that I'm using a library that needs to do recursive work where it uses apply/call functions multiple times.
I understand that the functions are converted to native calls and there is stack limit of 128. On certain situations I hit the limit and got the error.

It works fine if I use JavaScriptCore as the engine, I did a rough test in the stack limit seems to be around ~2738 when using JSC.

Is there a way to increase the limit when using Hermes or any other workaround?
Unfortunately, I can't change a 3rd party library that my app depends on.

Here is a repo with issue: https://github.com/vmalvaro/hermes-range-error

Thanks!

@tmikov
Copy link
Contributor

tmikov commented Jun 4, 2023

@vmalvaro the limit can be increased by changing Hermes as described in this comment.

@alvarovillafane
Copy link

@tmikov thanks for the quick response, I'm a bit of a newbie with Hermes so I'd appreciate your guidance.

This #135 means that I have to do that change in hermes/include/hermes/VM/Runtime.h, then a custom build of Hermes + react native from source as described here https://hermesengine.dev/docs/react-native-integration?

@neildhar
Copy link
Contributor

neildhar commented Jun 4, 2023

@vmalvaro

custom build of Hermes + react native from source as described here

Those instructions are unfortunately no longer accurate for newer versions of RN, which have changed how Hermes is consumed. If you're on an RN version that is 0.69+, you can set an environment variable to point RN to the source directory where you want it to get Hermes from. See https://github.com/facebook/react-native/blob/fd9e295befcd8781190ec26a6a2fc4ef39fb1c15/packages/react-native/ReactAndroid/hermes-engine/build.gradle#L42.

@Villar74
Copy link

screen

Get this issue when disabling js DEV mode

 System:
    OS: macOS 13.4
    CPU: (12) arm64 Apple M2 Pro
  Binaries:
    Node: 20.0.0 - /opt/homebrew/bin/node
    Yarn: 1.22.19 - /opt/homebrew/bin/yarn
    npm: 9.6.4 - /opt/homebrew/bin/npm
    Watchman: 2023.05.01.00 - /opt/homebrew/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK:
      API Levels: 31, 33
      Build Tools: 30.0.2, 30.0.3, 31.0.0, 33.0.0, 33.0.2
      System Images: android-33 | Google APIs ARM 64 v8a
  IDEs:
    Android Studio: Flamingo 2022.2.1 Patch 1 Flamingo 2022.2.1 Patch 1
    Xcode: 14.3/14E222b - /usr/bin/xcodebuild
  npmPackages:
    react: 18.2.0 => 18.2.0 
    react-native: 0.71.8 => 0.71.8 

@tmikov
Copy link
Contributor

tmikov commented Jul 12, 2023

@Villar74 is your problem related to loading JSON, which is the topic of this specific issue?

If not, and you believe there is a bug in Hermes, please create a new issue, preferably with more details, including source reproducing the problem. It is almost impossible to diagnose a problem from a screenshot of a stack trace of a compiled bundle.

@tmikov
Copy link
Contributor

tmikov commented Aug 23, 2023

Finally, this long standing problem has been fixed in 3213794 and should be available in the next release of RN.

@tmikov tmikov closed this as completed Aug 23, 2023
facebook-github-bot pushed a commit that referenced this issue Aug 24, 2023
Summary:
Now that the general case is fast, and generates much better code in
debug mode, stop emitting `AllocObjectLiteral` for object literals in
IRGen. This allows us to compile large JSON files with reasonable
performance and output in both release and debug builds.

Closes #135

Reviewed By: tmikov

Differential Revision: D47724425

fbshipit-source-id: 903eec6828043828965f8afa8e751ead67470d14
@Yashtoddle
Copy link

Hey tmikov could you please send this update in the coming patch as I needed this issue to be resolved in our codebase

@tmikov
Copy link
Contributor

tmikov commented Oct 4, 2023

@Yashtoddle I don't understand what you mean. Send what where?

@Yashtoddle
Copy link

Yashtoddle commented Oct 4, 2023

Sorry for not explaining the stuff clearly @tmikov actually we are using Hermes in our project as the engine and now we are facing the maximum call stack size exceded error in the android app and this error is now being resolved it would be great if you send 3213794
in the coming patch of the react native as we need this urgently in our project

@tmikov
Copy link
Contributor

tmikov commented Oct 4, 2023

@Yashtoddle we don't really control which changes are picked into each patch releases. You also need to be more specific about the version where you want this.

In any case I think you can request a pick here: https://github.com/reactwg/react-native-releases/discussions .

This is not a trivial bugfix, it is several commits including 18aa617 .

@neildhar @cortinico what is the feasibility of picking this?

@neildhar
Copy link
Contributor

neildhar commented Oct 4, 2023

@Yashtoddle this will be included in the upcoming RN 0.73. The feasibility of picking this fix into an earlier release will likely depend heavily on which RN version you are currently on.

neildhar added a commit to neildhar/hermes that referenced this issue Oct 19, 2023
Summary:
Original Author: neildhar@meta.com
Original Git: bbe7966

Now that the general case is fast, and generates much better code in
debug mode, stop emitting `AllocObjectLiteral` for object literals in
IRGen. This allows us to compile large JSON files with reasonable
performance and output in both release and debug builds.

Closes facebook#135

Original Reviewed By: tmikov

Original Revision: D47724425

Differential Revision: D50460149
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests