ReactiveX/rxjs · error · TypeError

MarbleContext.setInterval: callback must be a function

Error message

MarbleContext.setInterval: callback must be a function

What it means

The TestScheduler's patched setInterval requires a function callback, matching strict modern timer semantics. Passing a string (old IE-style eval form), null, or undefined throws instead of scheduling an interval.

Source

Thrown at packages/rxjs/src/testing/index.ts:90

      return new Promise((resolve) => {
        patched(() => resolve(value!), delay ?? 0);
      });
    };

    return patched;
  })();

  #clearTimeout: typeof globalThis.clearTimeout = (id: Parameters<typeof originalClearTimeout>[0]) => {
    if (id === undefined) return;
    this.#removeTimer(+id);
  };

  #setInterval: typeof globalThis.setInterval = (() => {
    const patched = (callback: TimerHandler, delay = 0, ...args: any[]): any => {
      const id = ++this.#timerId;
      const time = this.#now + delay;
      if (typeof callback !== 'function') {
        throw new TypeError('MarbleContext.setInterval: callback must be a function');
      }
      const item: TimerQueueItem = { id, callback: callback as (...args: any[]) => void, delay, time, type: 'interval', args };
      this.#addTimer(item);

      if (this.#shouldUseNodeTimeout) {
        let ref = false;
        const nodeTimeout = {
          ref: () => {
            ref = true;
            return nodeTimeout;
          },
          unref: () => {
            ref = false;
            return nodeTimeout;
          },
          hasRef: () => {
            return ref;
          },

View on GitHub (pinned to 54796b38a5)

Solutions

  1. Pass a function reference or arrow function: setInterval(() => tick(), 1000)
  2. Guard forwarding wrappers: if (typeof cb === 'function') setInterval(cb, ms)
  3. Initialize callback variables before use; make callback params required in types

Example fix

// before
setInterval('tick()', 1000);
// after
setInterval(tick, 1000); // or setInterval(() => tick(), 1000)
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof callback !== 'function') throw new TypeError('callback must be a function');
setInterval(callback, delay);

Type guard

const isFn = (cb: unknown): cb is (...args: any[]) => void => typeof cb === 'function';

Prevention

When it happens

Trigger: Inside TestScheduler.run(() => { setInterval('tick()', 1000) }) or setInterval(undefined, 1000) from a forwarding bug in code under test.

Common situations: Porting legacy code that used string callbacks with setInterval, or wrappers like myInterval(cb) that pass an uninitialized cb variable.

Related errors


AI-assisted analysis of ReactiveX/rxjs@54796b38a5 (2026-08-28). Data as JSON: /api/errors/95faa9ec969ee3df. Report an issue: GitHub.