{"record":{"id":"b9fba105ceb78e02","repo":"BabylonJS/Babylon.js","slug":"invalid-call-to-prewarmplayerasync-player-is-alr","errorCode":null,"errorMessage":"Invalid call to preWarmPlayerAsync - player is already playing or disposed","messagePattern":"Invalid call to preWarmPlayerAsync - player is already playing or disposed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/lottiePlayer/src/player.ts","lineNumber":67,"sourceCode":"     * If OffscreenCanvas is not supported by the browser, the animation will not play. Try using LocalLottiePlayer instead.\r\n     * @throws Error if OffscreenCanvas is not supported\r\n     */\r\n    public constructor() {\r\n        // Check if OffscreenCanvas is supported\r\n        if (!(\"OffscreenCanvas\" in window)) {\r\n            throw new Error(\"OffscreenCanvas not supported - cannot create Player\");\r\n        }\r\n    }\r\n\r\n    /**\r\n     * Pre-warms the worker and base renderer code. Animation-specific text or image renderer chunks\r\n     * load later, after the animation data is known.\r\n     * @returns A Promise that resolves to this Player instance when the worker is ready\r\n     * @throws Error if the player is already playing or disposed\r\n     */\r\n    public async preWarmPlayerAsync(): Promise<Player> {\r\n        if (this._playing || this._disposed) {\r\n            throw new Error(\"Invalid call to preWarmPlayerAsync - player is already playing or disposed\");\r\n        }\r\n\r\n        if (this._preWarmed) {\r\n            return this;\r\n        }\r\n\r\n        // Pre-warming already in progress\r\n        if (this._preWarmPromise) {\r\n            return await this._preWarmPromise;\r\n        }\r\n\r\n        // Create the promise that will be resolved when we receive the \"loaded\" message\r\n        this._preWarmPromise = new Promise<Player>((resolve, reject) => {\r\n            this._preWarmResolve = resolve;\r\n            this._preWarmReject = reject;\r\n        });\r\n\r\n        // Initialize worker if not already done\r","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/lottiePlayer/src/player.ts#L49-L85","documentation":"preWarmPlayerAsync() prepares the lottie worker player ahead of the actual load. The library throws this error because pre-warming is only valid on a fresh, idle player: if the player is already playing or has been disposed, there is nothing to pre-warm and the call is a programming mistake. It is an explicit guard on _playing/_disposed state at the top of the method.","triggerScenarios":"Calling preWarmPlayerAsync() after play() has started (_playing === true) or after dispose() (_disposed === true). Note a second call is safe only via the _preWarmed short-circuit, which is only reachable when not playing/disposed.","commonSituations":"Pre-warming inside a lifecycle hook that can fire after disposal (e.g. component unmount raced with init); calling pre-warm after the animation already started; reusing a disposed Player instance from a cache.","solutions":["Only call preWarmPlayerAsync() immediately after constructing the Player, before play()/load().","Check player state first: skip the call when player.isDisposed?.() or when playback started.","Guard the call site with a disposed/playing flag in your component lifecycle.","Create a fresh Player instance if the old one was disposed."],"exampleFix":"// before\nawait player.preWarmPlayerAsync();\n// after\nif (!player._disposed && !player._playing) {\n    await player.preWarmPlayerAsync();\n}","handlingStrategy":"validation","validationCode":"function canPreWarm(p: Player): boolean {\n    return !p._disposed && !p._playing;\n}\nif (canPreWarm(player)) await player.preWarmPlayerAsync();","typeGuard":"function isPreWarmable(p: Player): p is Player & { _disposed: false; _playing: false } {\n    return !(p as any)._disposed && !(p as any)._playing;\n}","tryCatchPattern":"try {\n    await player.preWarmPlayerAsync();\n} catch (e) {\n    if (e instanceof Error && e.message.includes('preWarmPlayerAsync')) {\n        // player already active — safe to ignore\n    } else throw e;\n}","preventionTips":["Pre-warm only at construction time, before any play/load call.","Track disposed state in your app layer and skip pre-warm after dispose.","Avoid pre-warming in async callbacks that may run after unmount/dispose.","Cache pre-warmed players and never reuse disposed instances."],"tags":["state-machine","lifecycle","webgl","animation"],"backgroundTag":"player-already-disposed","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}