Skip to content

Conversation

@jcorbin
Copy link

@jcorbin jcorbin commented Nov 21, 2025

I've managed to at least turn a couple of banal type programming bugs into a perhaps more diagnostic semantic failure of the test:

  • looks like _enable is no longer a thing, but the new class code does have a disable method
  • not sure how the kResourceStore property setter used to get called, but looks like it doesn't anymore...
  • ...so all those getStoreMap(this).get // or set or whatever sites that were pledged a non-undefined blew up

This gets us to "at least now it's just our own expectation that's failing, rather than the test crashing" like:

  ✘ [fail]: async_hooks › AsyncLocalStorage patch
  ✔ async_hooks › async_hooks Promise patch (616ms)
  ✔ bundle › Can be bundled (594ms)
  ─

  async_hooks › AsyncLocalStorage patch

  Difference (- actual, + expected):

  - undefined
  + 1

  [object Object]
    at packages/init/test/async_hooks.test.js:103:7
    at async packages/init/test/async_hooks.test.js:88:3

  ─

  1 test failed

The prior claimed type was a lie, as Map.get can return undefined, but
typescipt apparently cannot tell that thru bind...
@jcorbin
Copy link
Author

jcorbin commented Nov 21, 2025

For reference that debug print currently spits:

WAT AsyncLocalStorage {} { _propagate: [Function: _propagate] } [class AsyncLocalStorage] class AsyncLocalStorage {
  #defaultValue = undefined;
  #name = undefined;

  /**
   * @typedef {object} AsyncLocalStorageOptions
   * @property {any} [defaultValue] - The default value to use when no value is set.
   * @property {string} [name] - The name of the storage.
   */
  /**
   * @param {AsyncLocalStorageOptions} [options]
   */
  constructor(options = {}) {
    validateObject(options, 'options');
    this.#defaultValue = options.defaultValue;

    if (options.name !== undefined) {
      this.#name = `${options.name}`;
    }
  }

  /** @type {string} */
  get name() { return this.#name || ''; }

  static bind(fn) {
    return AsyncResource.bind(fn);
  }

  static snapshot() {
    return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
  }

  disable() {
    AsyncContextFrame.disable(this);
  }

  enterWith(data) {
    const frame = new AsyncContextFrame(this, data);
    AsyncContextFrame.set(frame);
  }

  run(data, fn, ...args) {
    const prior = this.getStore();
    if (ObjectIs(prior, data)) {
      return ReflectApply(fn, null, args);
    }
    this.enterWith(data);
    try {
      return ReflectApply(fn, null, args);
    } finally {
      this.enterWith(prior);
    }
  }

  exit(fn, ...args) {
    return this.run(undefined, fn, ...args);
  }

  getStore() {
    const frame = AsyncContextFrame.current();
    if (!frame?.has(this)) {
      return this.#defaultValue;
    }
    return frame?.get(this);
  }
}

@kriskowal kriskowal changed the title Node v24 hack Investigation for Node v24 AsyncLocalStorage compatibility issue Nov 21, 2025
@mhofman
Copy link
Contributor

mhofman commented Nov 25, 2025

I recently heard that async_hooks no longer gets triggered by starting the debugger, so we may be able to simply nuke the async_hooks patch out of existence.

Opened #3012

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants