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

makeShareableCloneRecursive Call Stack Size Exceeded #4177

Closed
SamuelScheit opened this issue Mar 6, 2023 · 28 comments
Closed

makeShareableCloneRecursive Call Stack Size Exceeded #4177

SamuelScheit opened this issue Mar 6, 2023 · 28 comments
Labels
Missing info The user didn't precise the problem enough Missing repro This issue need minimum repro scenario Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS

Comments

@SamuelScheit
Copy link

Description

I've switched from version 2.14.4 to the latest version 3.0.1 and get the error Maximum call stack size exceeded in the function makeShareableCloneRecursive from reanimated.

Steps to reproduce

I can't seem to isolate the issue, but it happened after upgrading to the latest version.
I've updated the Pods and reinstalled the App.
What could cause this kind of issue?

Snack or a link to a repository

Reanimated version

3.0.1

React Native version

0.71.0

Platforms

iOS

JavaScript runtime

Hermes

Workflow

React Native (without Expo)

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

Real device

Device model

iPhone 12 mini

Acknowledgements

Yes

@SamuelScheit SamuelScheit added the Needs review Issue is ready to be reviewed by a maintainer label Mar 6, 2023
@github-actions github-actions bot added Missing repro This issue need minimum repro scenario Missing info The user didn't precise the problem enough labels Mar 6, 2023
@github-actions
Copy link

github-actions bot commented Mar 6, 2023

Hey! 👋

The issue doesn't seem to contain a minimal reproduction.

Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

@github-actions
Copy link

github-actions bot commented Mar 6, 2023

Hey! 👋

It looks like you've omitted a few important sections from the issue template.

Please complete Snack or a link to a repository section.

@github-actions github-actions bot added the Platform: iOS This issue is specific to iOS label Mar 6, 2023
@jb4e
Copy link

jb4e commented Mar 6, 2023

We hit this as well when attempting to upgrade from v3 RC1. Haven't had a chance to dig in yet, but we will add more details if we can figure out a minimum repro.

@TomCorvus
Copy link

TomCorvus commented Mar 6, 2023

Hi,

It occurs on Android too in debug mode. When you have this error, all animation on screen do not work.
No problem on release mode.

I will try to reproduce this except if you know where it can come from.

@philipheinser
Copy link

philipheinser commented Mar 6, 2023

Added a log to the shareable clone and seeing this:

me": null, "_threadCount": 0, "displayName": "ReactRedux"}
 LOG  key $$typeof
 LOG  element Symbol(react.context)
 LOG  key _currentValue
 LOG  element null
 LOG  key _currentValue2
 LOG  element null
 LOG  key _threadCount
 LOG  element 0
 LOG  key Provider
 LOG  element {"$$typeof": Symbol(react.provider), "_context": {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": [Circular], "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}}
 LOG  value {"$$typeof": Symbol(react.provider), "_context": {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": [Circular], "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}}
 LOG  key $$typeof
 LOG  element Symbol(react.provider)
 LOG  key _context
 LOG  element {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": {"$$typeof": Symbol(react.provider), "_context": [Circular]}, "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}
 LOG  value {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": {"$$typeof": Symbol(react.provider), "_context": [Circular]}, "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}
 LOG  key $$typeof
 LOG  element Symbol(react.context)
 LOG  key _currentValue
 LOG  element null
 LOG  key _currentValue2
 LOG  element null
 LOG  key _threadCount
 LOG  element 0
 LOG  key Provider
 LOG  element {"$$typeof": Symbol(react.provider), "_context": {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": [Circular], "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}}
 LOG  value {"$$typeof": Symbol(react.provider), "_context": {"$$typeof": Symbol(react.context), "Consumer": {"$$typeof": Symbol(react.context), "_context": [Circular]}, "Provider": [Circular], "_currentRenderer": {}, "_currentRenderer2": null, "_currentValue": null, "_currentValue2": null, "_defaultValue": null, "_globalName": null, "_threadCount": 0, "displayName": "ReactRedux"}}

was accessing a redux prop moving it value into a useSharedValue solved the problem for me. was fine in the rc of version3

@pelayomartinez
Copy link

Commenting to follow. Also getting this error on Expo development client

@ranaavneet
Copy link
Sponsor

ranaavneet commented Mar 9, 2023

+1

@andrewlo
Copy link
Contributor

andrewlo commented Mar 9, 2023

It might be related to PanGestureHandler. When I remove PanGestureHandler from the screen where this error is happening, the error doesn't occur any more

@TomCorvus
Copy link

Interesting @andrewlo, I'm using react-native-pager-view on the screen where this message appears.
Maybe a clue to reproduce this on minimal repo.

@azcarraga
Copy link

In our case, I was able to narrow down to two different places where this error occurs. One was a Gesture.Pan.onEnd(), and the other was a useDerivedValue(). I narrowed down in both to a single line of code - and in both cases that line of code was referencing a prop from the containing component.

Could be coincidence, but @andrewlo @TomCorvus it might be worth checking if you can narrow down to the line of code inside the PanGestureHandler that's causing the failure.

I've still had no luck with a min repro. We had to stay on our previous build for now.

@TwistedMinda
Copy link

TwistedMinda commented Mar 10, 2023

Same here, happens with the lib "react-native-graph" when trying to migrate to reanimated v3.
I guess we might need to wait a little bit that all the libraries adapt to the new reanimated v3

@surethink
Copy link

got the same issue, any hero can fix it?

@SamuelScheit
Copy link
Author

SamuelScheit commented Mar 28, 2023

The cause of the issue is a recursive object that is being used in a worklet function (useAnimatedStyle)

function Test(props) {
	const obj = {
		opacity: 1,
	}
	obj.test = obj; // make the object recursive

	const style = useAnimatedStyle(() => ({
		opacity: obj.opacity,
	}));
	return <Animated.View style={style} />
}

To workaround this issue you have to convert the variable to serializable value, e.g. with object destructuring:

function Test(props) {
	const obj = {
		opacity: 1,
	}
	obj.test = obj; // make the object recursive

	const { opacity } = obj

	const style = useAnimatedStyle(() => ({
		opacity: opacity,
	}));

	return <Animated.View style={style} />
}

Reanimated V2 seems to automatically detect recursive objects and handle this edge case, but V3 does not.

@canpoyrazoglu
Copy link

canpoyrazoglu commented Apr 1, 2023

In my case the following code (inside useEffect) causes the same error:

    const [displayedChildren, setDisplayedChildren] = useState(props.children);
    const appearanceAnimation = useSharedValue(1);
    useEffect(() => {
        if(props.children != displayedChildren){
            appearanceAnimation.value = withTiming(0, {
                duration: TRANSITION_DURATION,
                easing: Easing.ease
            }, (finished) => {
                if(finished){
                    setDisplayedChildren(props.children);
                    appearanceAnimation.value = withTiming(1, {
                        duration: TRANSITION_DURATION
                    })
                }
            });
            
        }
    }, [props.children]);

I'm also new to Reanimated so I don't know how to solve it. First I though it was a recursive state update at my side, then I've seen this:

Simulator Screen Shot - iPhone SE (3rd generation) - 2023-04-01 at 13 05 21

Any help to get rid of this would be appreciated.

@SudoPlz
Copy link

SudoPlz commented Apr 6, 2023

I think the way to reproduce it is the following:

Example:
Assume that we have 2 class components, Component A and Component B.

  • Component A contains Component B within its render function.
  • Component A passes one of it's methods as a property to Component B
class ComponentA extends Component {
  doSomethingCool = () => {
    'worklet';
    // all the cool things
    return 'cool';
  }
  render() {
    return (<ComponentB
      doSomethingCoolProp={this.doSomethingCool}
    />)
  }
}

then in componentB you do:

const ComponentB = () => {

  const anotherFunc = () => {
    'worklet';
    const cool = this.props.doSomethingCoolProp();
  }

  runOnUI(anotherFunc);
  // ...
}

As soon as doSomethingCool is called we'll get the above error.

In my example anotherFunc is invoked by react-native-gesture-handler's Gesture.Pan().onEnd() so I'm not running runOnUI manually, but I think it's done below the hood. Still that's when I get this error.

@TwistedMinda
Copy link

For me it was popping when trying to install reanimated v3 with react-native < 0.71

Now that I've successfully migrated to 0.71.4 I don't have this error anymore -- no code changed

@SudoPlz
Copy link

SudoPlz commented Apr 10, 2023

For me it was popping when trying to install reanimated v3 with react-native < 0.71

Now that I've successfully migrated to 0.71.4 I don't have this error anymore -- no code changed

Definitely not our case. That's happening on our end and we're on 0.71.4 and reanimated v3.

@SudoPlz
Copy link

SudoPlz commented Apr 10, 2023

Also, I noticed that sometimes, even converting a method from a const arrow function to an old school function like so:

from:

class ComponentC extends Component {
  doSomethingCool = () => {
    'worklet';
    // this breaks
    return 'cool';
  }
}

to:

class ComponentC extends Component {
  function doSomethingCool() {
    'worklet';
    // this works
    return 'cool';
  }
}

makes the error go away. (but not always)

@TomCorvus
Copy link

It happens with react-native-draggable-flatlist for me and this PR fixes the problem (computerjazz/react-native-draggable-flatlist#462)
If this problem occurs with a third-party library, a fix is maybe needed to be fully compatible with rea 3.

@SudoPlz
Copy link

SudoPlz commented Apr 10, 2023

It happens with react-native-draggable-flatlist for me and this PR fixes the problem (computerjazz/react-native-draggable-flatlist#462) If this problem occurs with a third-party library, a fix is maybe needed to be fully compatible with rea 3.

so it looks like the main takeaway here is to avoid passing props from outside of the context of the worklet if I my understanding is correct.

We can only pass params through the function parameters, and not from the outer scope.

@LeviWilliams
Copy link

Just chiming in here in case this helps somebody - when we upgraded to V3 we had these issues alongside the app just freezing. The issue in our case is that we were incorrectly modifying a shared value within useDerivedValue which causes an infinite loop as shared values will auto-update that worklet. Though, this somehow worked fine in v2. Example of what you shouldn't do:

const derived = useDerivedValue(() => {
...
sharedVal.value = 'whatever'
})

@canpoyrazoglu
Copy link

Also, I noticed that sometimes, even converting a method from a const arrow function to an old school function like so:

from:

class ComponentC extends Component {
  doSomethingCool = () => {
    'worklet';
    // this breaks
    return 'cool';
  }
}

to:

class ComponentC extends Component {
  function doSomethingCool() {
    'worklet';
    // this works
    return 'cool';
  }
}

makes the error go away. (but not always)

In my case I'm not using worklets at all (honestly I don't even know how they exactly work) but still get this.

@kmagiera
Copy link
Member

@LeviWilliams The issue with updates in useDerivedValue is resolved here #4358 and should be included in next 3. release

kmagiera added a commit that referenced this issue Apr 17, 2023
## Summary

This PR addresses a number of issues reported related to exceeding stack
when converting objects to shareables (e.g. in #4177).

This issue got introduced in Reanimated 3 after #4060 where we got rid
of filtering only attributes accessed in worklets to be captured by
worklets.

This PR implements a solution that filters only plain objects before
converting things to shareables. In all of the instances of this issue
we encountered, the crash was due to the fact we were leaking some react
internals (fiber nodes) into the captured variables (e.g. via React's
refs or otherwise). These objects are not of a plain object type and
hence we wouldn't be able to support transferring these on the UI
runtime anyways. With this change we are skipping these type of objects
and replace them with a "inaccessible object" which is implemented as a
proxy that throws on any attempt of accessing its attributes – this way
users will get an error if they do use or access such objects.

In addition we add a cyclic object detection mechanism. Currently cyclic
objects cannot be converted to sharables – they weren't supported in
Reanimated 2 either. We add it as a part of this change in case the
object prototype check wouldn't be sufficient in some cases. In a
scenario when cyclic object is captured by a worklet we will detect that
and throw a more descriptive error.


## Test plan

The below code illustrates a scenario when we are capturing react
internals via ref:
```js
function Mleko({ props }: any) {
  const style = useAnimatedStyle(() => {
    console.log(props)
    return {}
  })
  return (
    <View ref={props.ref} />
  );
}

const ref = useRef();
<Mleko props={{ ref }} />
```

Before this change the above coud would throw stack exceeded error. Now
this works ok.

In addition we tested cyclic detection by capturing the following
object:
```js
const one = {};
const two = {one};
one.two = two;
```

The above object would throw stack exceeded error. now it throws an
error that says that user is capturing cyclic object.


Lastly, we tested accessing non plain objects:
```js
class Something() {
  constructor() { this.ggg = 7 }
}
const something = new Something();

useAnimatedStyle(() => {
  console.log(something.ggg);
  return {};
});
```

---------

Co-authored-by: Tomek Zawadzki <tomasz.zawadzki@swmansion.com>
@azcarraga
Copy link

This appears to be fixed in 3.1 - I can no longer repro. Thank you!

fluiddot pushed a commit to wordpress-mobile/react-native-reanimated that referenced this issue Jun 5, 2023
## Summary

This PR addresses a number of issues reported related to exceeding stack
when converting objects to shareables (e.g. in software-mansion#4177).

This issue got introduced in Reanimated 3 after software-mansion#4060 where we got rid
of filtering only attributes accessed in worklets to be captured by
worklets.

This PR implements a solution that filters only plain objects before
converting things to shareables. In all of the instances of this issue
we encountered, the crash was due to the fact we were leaking some react
internals (fiber nodes) into the captured variables (e.g. via React's
refs or otherwise). These objects are not of a plain object type and
hence we wouldn't be able to support transferring these on the UI
runtime anyways. With this change we are skipping these type of objects
and replace them with a "inaccessible object" which is implemented as a
proxy that throws on any attempt of accessing its attributes – this way
users will get an error if they do use or access such objects.

In addition we add a cyclic object detection mechanism. Currently cyclic
objects cannot be converted to sharables – they weren't supported in
Reanimated 2 either. We add it as a part of this change in case the
object prototype check wouldn't be sufficient in some cases. In a
scenario when cyclic object is captured by a worklet we will detect that
and throw a more descriptive error.


## Test plan

The below code illustrates a scenario when we are capturing react
internals via ref:
```js
function Mleko({ props }: any) {
  const style = useAnimatedStyle(() => {
    console.log(props)
    return {}
  })
  return (
    <View ref={props.ref} />
  );
}

const ref = useRef();
<Mleko props={{ ref }} />
```

Before this change the above coud would throw stack exceeded error. Now
this works ok.

In addition we tested cyclic detection by capturing the following
object:
```js
const one = {};
const two = {one};
one.two = two;
```

The above object would throw stack exceeded error. now it throws an
error that says that user is capturing cyclic object.


Lastly, we tested accessing non plain objects:
```js
class Something() {
  constructor() { this.ggg = 7 }
}
const something = new Something();

useAnimatedStyle(() => {
  console.log(something.ggg);
  return {};
});
```

---------

Co-authored-by: Tomek Zawadzki <tomasz.zawadzki@swmansion.com>
@thespacemanatee
Copy link

thespacemanatee commented Jul 6, 2023

Is anyone getting this issue again? It's happening only on Android Release builds.

@devoren
Copy link

devoren commented Jul 11, 2023

@thespacemanatee me too, im getting this issue again

@a-eid
Copy link

a-eid commented Jul 11, 2023

getting a similar issue as well.

@fr3nzy
Copy link

fr3nzy commented Jul 23, 2023

I get this error when I'm running in debug mode, then using developer settings to change to release mode.

I find rebuilding and using react-native start --reset-cache tends to fix this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Missing info The user didn't precise the problem enough Missing repro This issue need minimum repro scenario Needs review Issue is ready to be reviewed by a maintainer Platform: iOS This issue is specific to iOS
Projects
None yet