{"record":{"id":"76e91b63aaf67615","repo":"liabru/matter-js","slug":"matter-runner-missing-required-global-window-requ","errorCode":null,"errorMessage":"Matter.Runner: missing required global window.requestAnimationFrame.","messagePattern":"Matter\\.Runner: missing required global window\\.requestAnimationFrame\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/core/Runner.js","lineNumber":234,"sourceCode":"     * @param {runner} runner\n     */\n    Runner.stop = function(runner) {\n        Runner._cancelNextFrame(runner);\n    };\n\n    /**\n     * Schedules the `callback` on this `runner` for the next animation frame.\n     * @private\n     * @method _onNextFrame\n     * @param {runner} runner\n     * @param {function} callback\n     * @return {number} frameRequestId\n     */\n    Runner._onNextFrame = function(runner, callback) {\n        if (typeof window !== 'undefined' && window.requestAnimationFrame) {\n            runner.frameRequestId = window.requestAnimationFrame(callback);\n        } else {\n            throw new Error('Matter.Runner: missing required global window.requestAnimationFrame.');\n        }\n\n        return runner.frameRequestId;\n    };\n\n    /**\n     * Cancels the last callback scheduled by `Runner._onNextFrame` on this `runner`.\n     * @private\n     * @method _cancelNextFrame\n     * @param {runner} runner\n     */\n    Runner._cancelNextFrame = function(runner) {\n        if (typeof window !== 'undefined' && window.cancelAnimationFrame) {\n            window.cancelAnimationFrame(runner.frameRequestId);\n        } else {\n            throw new Error('Matter.Runner: missing required global window.cancelAnimationFrame.');\n        }\n    };","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/liabru/matter-js/blob/acb99b6f8784c809b940f1d2cf745427e088e088/src/core/Runner.js#L216-L252","documentation":"Runner._onNextFrame schedules the next animation frame via window.requestAnimationFrame, which is required for the runner loop to advance. In environments where window or requestAnimationFrame does not exist (e.g. Node.js, Web Workers, headless/jsdom setups), the library deliberately throws instead of failing silently. This is an environment-capability guard, not a logic bug.","triggerScenarios":"Calling Matter.Runner.run() (which invokes _onNextFrame) in Node.js, in a Web Worker, in a test runner using jsdom without RAF polyfilled, or in a browser so old it lacks requestAnimationFrame.","commonSituations":"Server-side rendering or running physics simulation headless; unit tests in jsdom (no native RAF); custom engines/environments without a DOM window.","solutions":["Run the code in a browser environment where window.requestAnimationFrame exists.","Polyfill requestAnimationFrame before running: global.window = { requestAnimationFrame: cb => setTimeout(cb, 1000/60), cancelAnimationFrame: id => clearTimeout(id) }.","For headless simulation, drive the engine manually with Engine.update(engine, delta) in a setInterval/loop instead of Runner.run.","In jsdom tests, install a RAF polyfill (e.g. requestanimationframe polyfill package or mock)."],"exampleFix":"// before (Node.js)\nconst runner = Matter.Runner.create();\nMatter.Runner.run(runner, engine); // throws\n// after\nEngine.update(engine, 1000 / 60); // manual stepping, or polyfill window.requestAnimationFrame","handlingStrategy":"fallback","validationCode":"if (typeof window === 'undefined' || typeof window.requestAnimationFrame !== 'function') {\n  // headless: drive manually or polyfill\n  globalThis.window = globalThis.window || {};\n  window.requestAnimationFrame = window.requestAnimationFrame || (cb => setTimeout(() => cb(Date.now()), 16));\n}","typeGuard":"function hasRaf(env) {\n  return typeof env !== 'undefined' && env !== null && typeof env.requestAnimationFrame === 'function';\n}","tryCatchPattern":"try {\n  Matter.Runner.run(runner, engine);\n} catch (e) {\n  if (String(e.message).includes('requestAnimationFrame')) {\n    (function loop(t) { Matter.Engine.update(engine, 1000 / 60); setTimeout(loop, 16); })();\n  } else { throw e; }\n}","preventionTips":["Only call Runner.run in browser/DOM contexts","Polyfill requestAnimationFrame when targeting Node, Workers, or jsdom","For headless simulation, step with Engine.update on your own timer instead of Runner"],"tags":["environment","requestanimationframe","browser-api","node"],"backgroundTag":"missing-requestanimationframe","analyzedSha":"acb99b6f8784c809b940f1d2cf745427e088e088","analyzedAt":"2026-09-02T21:29:27.057Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}