Skip to content

EffectScope stop() causes runtime crash (Cannot read properties of undefined) when dynamic scope cleanup mutates parent.scopes array during traversal #15083

Description

@Askerka00

Vue version

3.5.28

Link to minimal reproduction

https://play.vuejs.org/#eNqFVN1u0zAUfpUjCymdtKVsBYRCW37GLuAC0DrBTS6WOaeJN9e2bKftVPWCR+SNOHaStmzTkNrY8fnOd36+E2/YR2PSZYMsY2PHrTAeHPrGTHMlFkZbDxvA+Ry5n3Ft8Bi0ipvPwhnt6N3iHLYwt3oBCfEkucoV18oRjy9842ASIIPkXAp+B96KqkILf37bwtXgNdhGga8RPDqfJkd79w56HoETGBzBZAqbXEFHnC4L2SBZkstGKaGqSEHxAcK/JTGFReUJdFDDIAaBzpZSAoMD8t6T10KWp489HyDO/osYPR2dMDHC4/itt5aYSl0NdtW18NChFvSvED1FrL1jifmlzmtDxuGQ2iwcrISUsemrGlXfgxbTu267XbuGZ1i9ve8zfMLroSazhnN0LsqxBV54XsMArT3qOR44XPOoc4meGoVlBi82hE4XxFFUuL3etzU0hkzaRrrInyv6jYft/NLk0ovHhZGFR3oDGJdiSQHvJU5yZoqypH5mcPbSrN/lLCIIU4+mPymVEVzsxaKiiyVaV0ho87NorC4b7oWmgOTSOd803msFH3iYcgpyOLw5exz7rVnD6RuKD7yxTtsMjBbKo90nBHDVfSwtSxto2Ebq45od9aKwlVAnJEkGp68D81wrf7JCUdU+gxsty0PuWWx/BmNnCgVZz7KhFsuQTicPLda7X8LXgyR2IDmC95BYLBPIIKksokpIADbdbPoPfrslKYi1z3EYLpOwkgq0Gw932rBj5h1pOhdVeuu0oksoTkfOuF4YIdF+N6HTLmdZPzc5K6TUq6/xzNsGj/tzXiO/e+L81q3DWc5+WHRol5iznY3Kq9C35ovZN1zTfmdckNKS0M8YL5HmsQk5trBPjSop7QNczPZLvEpJ+St3sfaoXF9USDSOcMTnjG7Q82dK36c7Sl/1o8+2fwEpfeZ+

Steps to reproduce

Open the reproduction link.
Click the "Trigger сrash" button.
Observe the output status on the screen or check the browser console.

What is expected?

The parent scope should stop all its children successfully. When a child scope stops a sibling scope during the cleanup phase, the parent traversal loop should handle the array mutation gracefully without throwing errors.

What is actually happening?

The application crashes with the following error: TypeError: Cannot read properties of undefined (reading 'stop')

This happens because EffectScope.stop loops over this.scopes using a cached length l. During iteration, one child's onScopeDispose calls stop() on its sibling. The sibling removes itself from the parent's scopes array, reducing the array size. When the parent loop continues to the cached index l, it tries to access an element out of bounds (which is undefined), causing the runtime crash.

System Info

System: OS: macOS, Browser: Chrome, Node: v20.19.5

Any additional comments?

The bug lies in packages/reactivity/src/effectScope.ts.

Inside EffectScope, the methods pause(), resume(), and stop() loop over this.scopes using a cached length without accounting for array mutations. Creating a shallow copy of the array before iterating (e.g. const scopes = this.scopes.slice()) safely resolves the issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.scope: reactivity

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions