denoland/deno · error · TypeError

ERR_INVALID_ARG_VALUE

ERR_INVALID_ARG_VALUE

Error message

The argument 'tracked' is not a tracked function. Received ${inspected}

What it means

CallTracker wraps each function passed to tracker.calls(fn, expected) in a tracking context stored in an internal WeakMap. reset(tracked) and getCalls(tracked) look the argument up in that map; an argument that was never registered — most commonly the original fn instead of the wrapper calls() returns — throws ERR_INVALID_ARG_VALUE with 'is not a tracked function'.

Source

Thrown at ext/node/polyfills/internal/assert/calltracker.js:87

        `executed ${this.#calls.length} time(s).`;
      return {
        message,
        actual: this.#calls.length,
        expected: this.#expected,
        operator: this.#name,
        stack: this.#stackTrace,
      };
    }
  }
}

class CallTracker {
  #callChecks = new SafeSet();
  #trackedFunctions = new SafeWeakMap();

  #getTrackedFunction(tracked) {
    if (!this.#trackedFunctions.has(tracked)) {
      throw new ERR_INVALID_ARG_VALUE(
        "tracked",
        tracked,
        "is not a tracked function",
      );
    }
    return this.#trackedFunctions.get(tracked);
  }

  reset(tracked) {
    if (tracked === undefined) {
      SetPrototypeForEach(this.#callChecks, (check) => check.reset());
      return;
    }

    this.#getTrackedFunction(tracked).reset();
  }

  getCalls(tracked) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Always keep the return value of tracker.calls(fn) and pass that wrapper to getCalls()/reset().
  2. Reassign in place — fn = tracker.calls(fn) — so every later reference is the tracked one.
  3. Consider node:test's mock module (mock.fn / mock.method) — CallTracker is deprecated in modern Node.

Example fix

// before
tracker.calls(handleRequest);   // wrapper discarded
handleRequest(req, res);         // calls original: nothing recorded
tracker.getCalls(handleRequest); // ERR_INVALID_ARG_VALUE: not a tracked function

// after
const handleTracked = tracker.calls(handleRequest);
handleTracked(req, res);
tracker.getCalls(handleTracked);  // recorded call arguments
Defensive patterns

Strategy: validation

Validate before calling

const trackedFns = new WeakSet();
function track(tracker, fn, expected = 1) {
  const wrapped = tracker.calls(fn, expected);
  trackedFns.add(wrapped);
  return wrapped;
}
function isTracked(fn) {
  return trackedFns.has(fn);
}
// before tracker.getCalls(x): if (!isTracked(x)) throw new Error('untracked fn');

Try / catch

try {
  tracker.getCalls(fn);
} catch (e) {
  if (e.code === 'ERR_INVALID_ARG_VALUE' && /tracked function/.test(e.message)) {
    // re-wrap fn with tracker.calls and use the returned wrapper
  } else throw e;
}

Prevention

When it happens

Trigger: tracker.getCalls(fn) or tracker.reset(fn) where fn was never wrapped — e.g. const wrapped = tracker.calls(fn); tracker.getCalls(fn); (original fn passed instead of the wrapper). Passing a function registered on a different CallTracker instance, or the pre-wrap reference kept alive by a refactor, produces the same throw.

Common situations: Refactors that keep a reference to the pre-wrap function; passing a function wrapped by a different tracker instance; asserting on the wrong export of a module.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/7f8f1a0ed6a1265c. Report an issue: GitHub.