Security Advisories์ถ์ฒ: GitHub Security Advisories์กฐํ์ 1
[effect] Effect `AsyncLocalStorage` context lost/contaminated inside Effect fibers under concurrent load with RPC
By GitHub2026๋
3์ 21์ผ
**[effect] Effect `AsyncLocalStorage` context lost/contaminated inside Effect fibers under concurrent load with RPC**
Versions effect: 3.19.15 @effect/rpc: 0.72.1 @effect/platform: 0.94.2 Node.js: v22.20.0 Vercel runtime with Fluid compute Next.js: 16 (App Router) @clerk/nextjs: 6.x Root cause Effect's MixedScheduler batches fiber continuations and drains them inside a single microtask or timer callback. The AsyncLocalStorage context active during that callback belongs to whichever request first triggered the scheduler's drain cycle โ not the request that owns the fiber being resumed. Scheduler batching (effect/src/Scheduler.ts, MixedScheduler) // MixedScheduler.starve() โ called once when first task is scheduled private starve(depth = 0) { if (depth >= this.maxNextTickBeforeTimer) { setTimeout(() => this.starveInternal(0), 0) // timer queue } else { Promise.resolve(void 0).then(() => this.starveInternal(depth + 1)) // microtask queue } } // MixedScheduler.starveInternal() โ drains ALL accumulated tasks in one call private starveInternal(depth: number) { const tasks = this.tasks.buckets this.tasks.buckets = [] for (const [_, toRun] of tasks) { for (let i = 0; i < toRun.length; i++) { toRun[i]() // โ Every fiber continuation runs in the SAME ALS context } } // ... } scheduleTask only calls starve() when running is false. Subsequent tasks accumulate in this.tasks until starveInternal drains them all...
---
**[devsupporter ํด์ค]**
์ด ๊ธฐ์ฌ๋ GitHub Security Advisories์์ ์ ๊ณตํ๋ ์ต์ ๊ฐ๋ฐ ๋ํฅ์ ๋๋ค. ๊ด๋ จ ๋๊ตฌ๋ ๊ธฐ์ ์ ๋ํด ๋ ์์๋ณด์๋ ค๋ฉด ์๋ณธ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
Versions effect: 3.19.15 @effect/rpc: 0.72.1 @effect/platform: 0.94.2 Node.js: v22.20.0 Vercel runtime with Fluid compute Next.js: 16 (App Router) @clerk/nextjs: 6.x Root cause Effect's MixedScheduler batches fiber continuations and drains them inside a single microtask or timer callback. The AsyncLocalStorage context active during that callback belongs to whichever request first triggered the scheduler's drain cycle โ not the request that owns the fiber being resumed. Scheduler batching (effect/src/Scheduler.ts, MixedScheduler) // MixedScheduler.starve() โ called once when first task is scheduled private starve(depth = 0) { if (depth >= this.maxNextTickBeforeTimer) { setTimeout(() => this.starveInternal(0), 0) // timer queue } else { Promise.resolve(void 0).then(() => this.starveInternal(depth + 1)) // microtask queue } } // MixedScheduler.starveInternal() โ drains ALL accumulated tasks in one call private starveInternal(depth: number) { const tasks = this.tasks.buckets this.tasks.buckets = [] for (const [_, toRun] of tasks) { for (let i = 0; i < toRun.length; i++) { toRun[i]() // โ Every fiber continuation runs in the SAME ALS context } } // ... } scheduleTask only calls starve() when running is false. Subsequent tasks accumulate in this.tasks until starveInternal drains them all...
---
**[devsupporter ํด์ค]**
์ด ๊ธฐ์ฌ๋ GitHub Security Advisories์์ ์ ๊ณตํ๋ ์ต์ ๊ฐ๋ฐ ๋ํฅ์ ๋๋ค. ๊ด๋ จ ๋๊ตฌ๋ ๊ธฐ์ ์ ๋ํด ๋ ์์๋ณด์๋ ค๋ฉด ์๋ณธ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
![[effect] Effect `AsyncLocalStorage` context lost/contaminated inside Effect fibers under concurrent load with RPC](/assets/images/github_com_1774224315917.png)