pear-devs/pear-desktop · error · TypeError

Positive number expected as fade duration!

Error message

Positive number expected as fade duration!

What it means

VolumeFader.setFadeDuration (also invoked from the constructor via options.fadeDuration) requires the fade length to be a number strictly greater than zero, in milliseconds. Zero, negatives, NaN, strings, or Infinity all throw this TypeError.

Source

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

  /**
   * Set fade duration.
   * (used for future calls to fadeTo)
   *
   * @param {Number} fadeDuration - fading length in milliseconds
   * @throws {TypeError} if fadeDuration is not a number greater than zero
   * @return {Object} VolumeFader instance for chaining
   */
  setFadeDuration(fadeDuration: number) {
    // If duration is a valid number > 0…
    if (!Number.isNaN(fadeDuration) && fadeDuration > 0) {
      // Set fade duration
      this.fadeDuration = fadeDuration;

      // Log setting
      this.logger?.('Set fade duration to ' + String(fadeDuration) + ' ms.');
    } else {
      // Abort and throw an exception
      throw new TypeError('Positive number expected as fade duration!');
    }

    // Return instance for chaining
    return this;
  }

  /**
   * Define a new fade and start fading.
   *
   * @param {Number} targetVolume - level to fade to in the range 0…1
   * @param {Function} callback - (optional) function to be called when fade is complete
   * @throws {TypeError} if targetVolume is not in the range 0…1
   * @return {Object} VolumeFader instance for chaining
   */
  fadeTo(targetVolume: number, callback?: () => void) {
    // Validate volume and throw if invalid
    validateVolumeLevel(targetVolume);

View on GitHub (pinned to 1e2aac5706)

Solutions

  1. Coerce and validate before passing: `Number.isFinite(d) && d > 0 ? d : 1000`
  2. If an instant switch is desired, use a tiny positive duration like 1 ms instead of 0
  3. Parse form/storage input with Number() and default on NaN

Example fix

// before
new VolumeFader(media, { fadeDuration: cfg.crossfadeMs }); // cfg value may be 0/NaN

// after
const d = Number(cfg.crossfadeMs);
new VolumeFader(media, { fadeDuration: Number.isFinite(d) && d > 0 ? d : 1000 });
Defensive patterns

Strategy: validation

Validate before calling

const parseFadeDuration = (v: unknown): number => {
  const n = Number(v);
  return Number.isFinite(n) && n > 0 ? n : 1000;
};

Type guard

const isPositiveNumber = (v: unknown): v is number =>
  typeof v === 'number' && Number.isFinite(v) && v > 0;

Try / catch

try { fader.setFadeDuration(d); } catch (e) { if (e instanceof TypeError && /fade duration/.test(e.message)) fader.setFadeDuration(1000); else throw e; }

Prevention

When it happens

Trigger: Passing {fadeDuration: 0} hoping for an instant switch; passing a negative or non-numeric value (e.g. '500' as a string from a form field); a NaN produced by `parseInt(userInput)` on empty input; options.fadeDuration coming from config that is null.

Common situations: User-configurable crossfade duration fields that allow 0 or empty input; config migration leaving undefined/null; durations loaded from storage as strings; unit tests passing 0 to mean 'immediate'.

Related errors


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