bilibili/flv.js · error · IllegalStateException

MediaSource has been attached to an HTMLMediaElement!

Error message

MediaSource has been attached to an HTMLMediaElement!

What it means

attachMediaElement() creates a new MediaSource and wires it to the given HTMLMediaElement. The library throws IllegalStateException if this MSEController instance already has a MediaSource, because a MediaSource can only ever be attached to one media element and one controller instance. It signals a lifecycle/reuse mistake rather than a runtime failure.

Source

Thrown at src/core/mse-controller.js:101

        if (this._mediaElement || this._mediaSource) {
            this.detachMediaElement();
        }
        this.e = null;
        this._emitter.removeAllListeners();
        this._emitter = null;
    }

    on(event, listener) {
        this._emitter.addListener(event, listener);
    }

    off(event, listener) {
        this._emitter.removeListener(event, listener);
    }

    attachMediaElement(mediaElement) {
        if (this._mediaSource) {
            throw new IllegalStateException('MediaSource has been attached to an HTMLMediaElement!');
        }
        let ms = this._mediaSource = new window.MediaSource();
        ms.addEventListener('sourceopen', this.e.onSourceOpen);
        ms.addEventListener('sourceended', this.e.onSourceEnded);
        ms.addEventListener('sourceclose', this.e.onSourceClose);

        this._mediaElement = mediaElement;
        this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);
        mediaElement.src = this._mediaSourceObjectURL;
    }

    detachMediaElement() {
        if (this._mediaSource) {
            let ms = this._mediaSource;
            for (let type in this._sourceBuffers) {
                // pending segments should be discard
                let ps = this._pendingSegments[type];
                ps.splice(0, ps.length);

View on GitHub (pinned to 42343088f2)

Solutions

  1. Create a new player (or new MSEController) instead of reusing the instance for a second attach
  2. Detach the element and destroy the old player before attaching: call player.unload() / player.detachMediaElement() / player.destroy(), then build a fresh player
  3. In UI frameworks, guard effects so attach runs once (or clean up in the effect's teardown by destroying the player)

Example fix

// before
const player = createPlayer(mds);
player.attachMediaElement(videoEl);
// later, same player:
player.attachMediaElement(newVideoEl); // throws
// after
player.destroy();
const player2 = createPlayer(mds);
player2.attachMediaElement(newVideoEl);
Defensive patterns

Strategy: validation

Validate before calling

if (player && typeof player.attachMediaElement === 'function' && !player._mediaElement && !player._mediaSource) {
  player.attachMediaElement(videoEl);
} else {
  player.destroy();
  player = createPlayer(mds, config);
  player.attachMediaElement(videoEl);
}

Type guard

function canAttach(player) {
  return player != null && typeof player.attachMediaElement === 'function' && !player._mediaSource;
}

Try / catch

try {
  player.attachMediaElement(videoEl);
} catch (e) {
  if (e.name === 'IllegalStateException') {
    player.destroy();
    player = createPlayer(mds, config);
    player.attachMediaElement(videoEl);
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling player.attachMediaElement(el) a second time on the same player instance (e.g. after re-attaching to a new <video> element, or a React re-render/mount calling attach again), or calling it after destroy() left stale state.

Common situations: React/Vue StrictMode double-mounting components that call attachMediaElement in an effect; hot module reloading; trying to re-point one player to another video element without recreating the player; calling attach on a reused singleton player.

Related errors


AI-assisted analysis of bilibili/flv.js@42343088f2 (2026-09-01). Data as JSON: /api/errors/b43fbd7b821631af. Report an issue: GitHub.