BabylonJS/Babylon.js · error

Invalid call to preWarmPlayerAsync - player is already playi

Error message

Invalid call to preWarmPlayerAsync - player is already playing or disposed

What it means

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.

Source

Thrown at packages/dev/lottiePlayer/src/player.ts:67

     * If OffscreenCanvas is not supported by the browser, the animation will not play. Try using LocalLottiePlayer instead.
     * @throws Error if OffscreenCanvas is not supported
     */
    public constructor() {
        // Check if OffscreenCanvas is supported
        if (!("OffscreenCanvas" in window)) {
            throw new Error("OffscreenCanvas not supported - cannot create Player");
        }
    }

    /**
     * Pre-warms the worker and base renderer code. Animation-specific text or image renderer chunks
     * load later, after the animation data is known.
     * @returns A Promise that resolves to this Player instance when the worker is ready
     * @throws Error if the player is already playing or disposed
     */
    public async preWarmPlayerAsync(): Promise<Player> {
        if (this._playing || this._disposed) {
            throw new Error("Invalid call to preWarmPlayerAsync - player is already playing or disposed");
        }

        if (this._preWarmed) {
            return this;
        }

        // Pre-warming already in progress
        if (this._preWarmPromise) {
            return await this._preWarmPromise;
        }

        // Create the promise that will be resolved when we receive the "loaded" message
        this._preWarmPromise = new Promise<Player>((resolve, reject) => {
            this._preWarmResolve = resolve;
            this._preWarmReject = reject;
        });

        // Initialize worker if not already done

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Only call preWarmPlayerAsync() immediately after constructing the Player, before play()/load().
  2. Check player state first: skip the call when player.isDisposed?.() or when playback started.
  3. Guard the call site with a disposed/playing flag in your component lifecycle.
  4. Create a fresh Player instance if the old one was disposed.

Example fix

// before
await player.preWarmPlayerAsync();
// after
if (!player._disposed && !player._playing) {
    await player.preWarmPlayerAsync();
}
Defensive patterns

Strategy: validation

Validate before calling

function canPreWarm(p: Player): boolean {
    return !p._disposed && !p._playing;
}
if (canPreWarm(player)) await player.preWarmPlayerAsync();

Type guard

function isPreWarmable(p: Player): p is Player & { _disposed: false; _playing: false } {
    return !(p as any)._disposed && !(p as any)._playing;
}

Try / catch

try {
    await player.preWarmPlayerAsync();
} catch (e) {
    if (e instanceof Error && e.message.includes('preWarmPlayerAsync')) {
        // player already active — safe to ignore
    } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/b9fba105ceb78e02. Report an issue: GitHub.