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.
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
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.