Repository navigation
Closed
Labels
bugSomething isn't workingSomething isn't working
Description
Test I ran
node als_leak.ts
slice run als_leak.tsals_leak.ts
import { AsyncLocalStorage } from "node:async_hooks";
const show = async (k: string, f: () => unknown) => { let v; try { v = await Promise.race([Promise.resolve().then(f), new Promise((r) => setTimeout(() => r("NEVER SETTLED (2000 ms)"), 2000))]); } catch (e: any) { v = "THROWS " + e.name + ": " + String(e.message).slice(0, 100); } console.log(k.padEnd(52) + JSON.stringify(v)); };
(async () => {
const als = new AsyncLocalStorage<string>();
await als.run("request-A", async () => { await new Promise((r) => setTimeout(r, 2)); });
await show("getStore() after `await als.run('request-A', asyncFn)`", () => String(als.getStore()));
await show("getStore() inside a later, unrelated setTimeout", () => new Promise((res) => setTimeout(() => res(String(als.getStore())), 5)));
await show("getStore() inside a later, unrelated promise job", () => Promise.resolve().then(() => String(als.getStore())));
const als2 = new AsyncLocalStorage<string>(); als2.run("sync", () => {});
await show("getStore() after a synchronous als.run(...)", () => String(als2.getStore()));
})();Result
Node v24.22.2:
getStore() after `await als.run('request-A', asyncFn)`"undefined"
getStore() inside a later, unrelated setTimeout "undefined"
getStore() inside a later, unrelated promise job "undefined"
getStore() after a synchronous als.run(...) "undefined"
Slice v0.10.7:
getStore() after `await als.run('request-A', asyncFn)`"request-A"
getStore() inside a later, unrelated setTimeout "request-A"
getStore() inside a later, unrelated promise job "request-A"
getStore() after a synchronous als.run(...) "undefined"
What is wrong
After await als.run("request-A", asyncFn) completes, code outside the run() scope still sees "request-A": in the awaiting function's continuation, in a later unrelated setTimeout, and in a later promise job. A synchronous als.run() restores correctly.
Propagation inside run() is correct (checked across await, setTimeout, queueMicrotask, and two concurrent run() calls keep separate stores). The defect is that the store is not restored for the code that awaited the run(). Since the store typically holds request-scoped data (user, tenant, trace id), whatever runs next in that continuation inherits the previous request's context.
Environment
- Slice
v0.10.7, commitea7c70c, built withcargo +1.96.0 build --release --locked --bin slice - Reference: Node.js
v24.22.2(same script, same machine) - Ubuntu 26.04, Linux 7.0, x86_64
- The same output is produced by the
v0.10.5tag, so this is not a regression in 0.10.6/0.10.7.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working
Activity
siktec-lab commentedon Nov 11, 2026
Addressed in #67 (�0.10.11): implemented HostMakeJobCallback and HostCallJobCallback on SliceHostHooks in �oa_realm.rs so AsyncLocalStorage snapshots are captured at .then() / �wait registration time (Promise::perform_promise_then) and restored across promise reaction jobs, external deferred promises, and timer callbacks.