liabru/matter-js · error · Error

Matter.Runner: missing required global window.cancelAnimatio

Error message

Matter.Runner: missing required global window.cancelAnimationFrame.

What it means

Runner._cancelNextFrame cancels a scheduled frame using window.cancelAnimationFrame, which must exist to stop the runner loop safely. When window or cancelAnimationFrame is unavailable (Node, Worker, jsdom), Runner.stop()/pause cannot cancel the pending callback and throws. It is the teardown counterpart of error 0.

Source

Thrown at src/core/Runner.js:250

            runner.frameRequestId = window.requestAnimationFrame(callback);
        } else {
            throw new Error('Matter.Runner: missing required global window.requestAnimationFrame.');
        }

        return runner.frameRequestId;
    };

    /**
     * Cancels the last callback scheduled by `Runner._onNextFrame` on this `runner`.
     * @private
     * @method _cancelNextFrame
     * @param {runner} runner
     */
    Runner._cancelNextFrame = function(runner) {
        if (typeof window !== 'undefined' && window.cancelAnimationFrame) {
            window.cancelAnimationFrame(runner.frameRequestId);
        } else {
            throw new Error('Matter.Runner: missing required global window.cancelAnimationFrame.');
        }
    };

    /**
     * Returns the mean of the given numbers.
     * @method _mean
     * @private
     * @param {Number[]} values
     * @return {Number} the mean of given values.
     */
    var _mean = function(values) {
        var result = 0,
            valuesLength = values.length;

        for (var i = 0; i < valuesLength; i += 1) {
            result += values[i];
        }

View on GitHub (pinned to acb99b6f87)

Solutions

  1. Ensure the environment provides both requestAnimationFrame AND cancelAnimationFrame on window.
  2. Polyfill both: window.cancelAnimationFrame = id => clearTimeout(id) alongside the RAF polyfill.
  3. Store the frameRequestId returned by Runner.run and call window.cancelAnimationFrame yourself in a compatible environment.
  4. In browsers, this should never occur — verify code is not accidentally running in a Worker context.

Example fix

// before (Node.js test)
Matter.Runner.stop(runner); // throws
// after
window.cancelAnimationFrame = id => clearTimeout(id); // polyfill first
Matter.Runner.stop(runner);
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof window === 'undefined' || typeof window.cancelAnimationFrame !== 'function') {
  globalThis.window = globalThis.window || {};
  window.cancelAnimationFrame = window.cancelAnimationFrame || (id => clearTimeout(id));
}

Type guard

function canCancelRaf(env) {
  return typeof env !== 'undefined' && env !== null && typeof env.cancelAnimationFrame === 'function';
}

Try / catch

try {
  Matter.Runner.stop(runner);
} catch (e) {
  if (String(e.message).includes('cancelAnimationFrame') && runner.frameRequestId) {
    clearTimeout(runner.frameRequestId);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling Runner.stop(runner) or Runner.pause(runner) in an environment lacking window.cancelAnimationFrame — typically Node, Web Worker, or jsdom tests.

Common situations: Stopping a runner in server-side code after a polyfilled start; cleaning up tests without a full RAF polyfill (started but cannot cancel).

Related errors


AI-assisted analysis of liabru/matter-js@acb99b6f87 (2026-09-02). Data as JSON: /api/errors/b94ba01bbc7b292d. Report an issue: GitHub.