CleanupTransactions RangeError: Maximum call stack size exceeded (Nested Objects & Arrays)

I am using SyncedStore to handle some relatively deep objects in my project. It works perfectly most of the time. However, when I am applying larger scale changes to my document (ie. adding large amounts of data), I frequently get the maximum call stack size exceeded error as shown in the title. I have trawled the forums for a similar issue, and I’ve come across `cleanupTransactions` causes "maximum call stack size exceeded" (+fix PR) · Issue #522 · yjs/yjs · GitHub, but it seems that this is intended to fix YText and not YMap/YArray, which I am using. Is there a workaround or better practice for this that I could apply to my transactions, where I could somehow split up the cleanupTransactions call so it doesn’t overflow the call stack? I’ve naively tried to split up my larger transactions into multiple concurrent transactions, but it doesn’t seem to affect anything, and I am looking for new ideas to approach this. Any help would be appreciated!

Edit: This doesn’t seem to be a specific bug associated with those larger scale changes, the CleanupTransactions call stack seems to be quite deep for smaller transactions, and simply seems to scale alongside the amount of data being modified at a time.

I think your best bet is to create a minimal test case that reproduces the issue. It definitely seems like a bug.

When you split a single transaction into multiple transactions you lose atomicity, so that’s only safe to do if the data is valid in between each transaction and interruptions are recoverable. Not sure if that applies in this case, but something to be aware of.

1 Like

Thanks for the reply and suggestions!

After some more digging, I think I have discovered the root cause of the bug. Because I’m (temporarily) locked into Vue 2, I have a doc.on(“update”) handler that copies the store data into Vue data using structuredClone in order to update the DOM (which does seem like really bad practice, but it was the easiest way for me to get it to work). I think I’ve managed to identify that calling structuredClone within the update handler invokes a huge stack of recursive cleanupTransactions calls. After swapping to our old friend JSON.parse(JSON.stringify()) to make the copy instead, the issue is now gone.

Edit: Actually, it seems that directly accessing store data in most ways while inside the update handler also causes a bunch of cleanupTransaction calls.

1 Like

I’m glad you figured it out. If you’re deep cloning YJS internals you’re definitely playing with fire! :fire::blush: