pear-devs/pear-desktop · error · TypeError

Media element expected!

Error message

Media element expected!

What it means

The VolumeFader constructor requires a real media element because it drives fades by writing to HTMLMediaElement.volume. Anything that is not an instanceof HTMLMediaElement (audio or video tag) is rejected with this TypeError immediately at construction.

Source

Thrown at src/plugins/crossfade/fader.ts:91

  private active: boolean = false;
  private fade: VolumeFade | undefined;

  /**
   * VolumeFader Constructor
   *
   * @param media {HTMLMediaElement} - audio or video element to be controlled
   * @param options {Object} - an object with optional settings
   * @throws {TypeError} if options.initialVolume or options.fadeDuration are invalid
   *
   */
  constructor(media: HTMLMediaElement, options: VolumeFaderOptions) {
    // Passed media element of correct type?
    if (media instanceof HTMLMediaElement) {
      // Save reference to media element
      this.media = media;
    } else {
      // Abort and throw an exception
      throw new TypeError('Media element expected!');
    }

    // Make sure options is an object
    options = options || {};

    // Log function passed?
    if (typeof options.logger === 'function') {
      // Set log function to the one specified
      this.logger = options.logger;
    } else {
      // Set log function explicitly to false
      this.logger = null;
    }

    // Linear volume fading?
    if (options.fadeScaling === 'linear') {
      // Pass levels unchanged
      this.scale = {

View on GitHub (pinned to 1e2aac5706)

Solutions

  1. Guard construction: `const el = document.querySelector('audio'); if (el instanceof HTMLMediaElement) new VolumeFader(el, ...)`
  2. Initialize the fader after the element exists (DOMContentLoaded, component mount, or player-ready callback)
  3. If dealing with iframes/multiple realms, use a duck-typed check ('play' in el && 'volume' in el) instead of instanceof
  4. Verify the selector/tag: the element must literally be an <audio> or <video> element

Example fix

// before
const fader = new VolumeFader(document.querySelector('#player'));

// after
const el = document.querySelector('#player');
if (!(el instanceof HTMLMediaElement)) throw new Error('player element not ready');
const fader = new VolumeFader(el);
Defensive patterns

Strategy: type-guard

Validate before calling

const el = document.querySelector<HTMLAudioElement>('audio');
if (!el) { /* element not mounted yet; init later */ }

Type guard

const isMediaElement = (el: Element | null): el is HTMLMediaElement =>
  el instanceof HTMLMediaElement;

Try / catch

try { fader = new VolumeFader(el); } catch (e) { if (e instanceof TypeError && /Media element/.test(e.message)) { retry after mount } else throw e; }

Prevention

When it happens

Trigger: Calling `new VolumeFader(document.querySelector('.audio'))` when the selector matched nothing (null) or matched a non-media element (a <div> or <source>); passing a jQuery wrapper, an element from another document/realm where instanceof fails; constructing before the DOM element exists.

Common situations: Running the script before the audio element is mounted (null from querySelector); grabbing the wrong node in a shadow DOM or iframe; passing a wrapped/extended element from another window context so the instanceof check fails; refactors that changed the tag from <audio> to a custom element.

Related errors


AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27). Data as JSON: /api/errors/ac4e804ac7e87535. Report an issue: GitHub.