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.

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions