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
- Guard construction: `const el = document.querySelector('audio'); if (el instanceof HTMLMediaElement) new VolumeFader(el, ...)`
- Initialize the fader after the element exists (DOMContentLoaded, component mount, or player-ready callback)
- If dealing with iframes/multiple realms, use a duck-typed check ('play' in el && 'volume' in el) instead of instanceof
- 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
- Construct the fader only after the <audio>/<video> element exists (mount/ready callback)
- Use querySelector<HTMLAudioElement>('audio') so TypeScript enforces the tag
- Never pass wrapped elements (jQuery, custom wrappers) directly
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
- Number between 0 and 1 expected as volume!
- Expected 'linear', 'logarithmic' or a positive number as fad
- Positive number expected as fade duration!
- plugins.downloader.backend.feedback.video-id-not-found
AI-assisted analysis of pear-devs/pear-desktop@1e2aac5706 (2026-08-27).
Data as JSON: /api/errors/ac4e804ac7e87535.
Report an issue: GitHub.