Skip to content

node:diagnostics_channel: preserve non-callable bindStore transform errors #3085

Closed

Description

@andrewtdiz

Summary

Channel#bindStore(store, transform) should remember any non-undefined transform value. During runStores(), Node attempts to call that transform; if it is not callable, the callback still runs with no store context and Node reports an uncaught TypeError: transform is not a function. Perry currently discards any non-closure transform at bind time, so null, numbers, strings, and other non-callable transform values behave exactly like an omitted transform and no error is reported.

Node Behavior

Probe run with Node v25.9.0:

const { channel } = require("node:diagnostics_channel");
const { AsyncLocalStorage } = require("node:async_hooks");

process.on("uncaughtException", (err) => {
  console.log("uncaught:", err.name, err.code || "no-code", err.message);
  process.exitCode = 0;
});

for (const transform of [null, 1]) {
  const ch = channel("bind-transform-" + Math.random());
  const als = new AsyncLocalStorage();
  ch.bindStore(als, transform);
  const ret = ch.runStores({ value: 1 }, () => {
    console.log("callback store", als.getStore());
    return "ret";
  });
  console.log("returned", ret);
}

Observed behavior: the callback runs and returns, als.getStore() is undefined, and Node emits uncaught TypeError events with message transform is not a function for both null and numeric transform values. An omitted or explicit undefined transform does not report that error.

Perry Behavior

  • crates/perry-runtime/src/node_submodules/diagnostics.rs::diag_channel_bind_store() computes let transform = if valid_closure_value(transform) { Some(transform) } else { None };.
  • That means every non-callable transform is stored as None, indistinguishable from an omitted transform.
  • diag_channel_run_stores() only attempts js_closure_call1(closure_ptr(t), data) when the stored transform is Some(t), so the Node TypeError path is skipped for non-callable transform values.
  • Current store fixtures cover a valid transform and a throwing function transform, but not non-callable transform values.

Suggested PR Cut

Batch this with related issues in:
node:diagnostics_channel: tracing and store semantics parity cut (#3242)

Good batch candidates:

Do not batch with:

  • unrelated AsyncLocalStorage semantics outside diagnostics store binding
  • worker/thread diagnostics subscriber policy changes
  • tracing-channel category work in node:trace_events

Acceptance

  • parity/regression test proves bindStore(store, null) followed by runStores() reports Node-compatible non-callable transform error behavior
  • parity/regression test proves a numeric or string transform has the same error path
  • omitted or explicit undefined transform remains the no-transform case
  • wrapped callback execution and return value behavior remain Node-compatible
  • related known-failure/docs/manifest entries updated if touched

Activity

  1. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    ContributorAuthor

    Taking this in a diagnostics_channel tracing/store semantics PR cut with #3082, #3086, and #3084.

  2. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    ContributorAuthor

    PR opened: #3503

  3. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    ContributorAuthor

    Implemented in #3504.

    Verification on the rebased branch:

    • PATH=/home/github-runner/actions-runner/externals/node24/bin:$PATH ./run_parity_tests.sh --suite node-suite --module diagnostics_channel passed: 64 pass, 0 fail, 0 compile fail.
    • RUSTC_WRAPPER= cargo check -p perry-runtime -p perry-codegen -p perry-hir -p perry-api-manifest -p perry passed with existing warnings.
    • cargo fmt --all -- --check, git diff --check && git diff --cached --check, ./scripts/check_file_size.sh, and jq empty test-parity/known_failures.json passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions