mozilla/pdf.js · error · Error

Invalid updateScale options: either `steps` or `scaleFactor`

Error message

Invalid updateScale options: either `steps` or `scaleFactor` must be provided.

What it means

PDFViewer.updateScale() changes the current zoom either by a multiplicative `scaleFactor` or by N discrete `steps` (powered by DEFAULT_SCALE_DELTA). Both parameters default to `null`, so the method refuses to guess and throws when neither is supplied. It is a contract guard: exactly one zoom strategy must be present. Note that `increaseScale`/`decreaseScale` always inject `steps`, so this only fires when `updateScale` is called directly with an empty or stripped options object.

Source

Thrown at web/pdf_viewer.js:2585

   * @property {number} [steps]
   * @property {Array} [origin] x and y coordinates of the scale
   *                            transformation origin.
   * @property {Array<number>} [pan] - Horizontal and vertical gesture deltas.
   */

  /**
   * Changes the current zoom level by the specified amount.
   * @param {ChangeScaleOptions} [options]
   */
  updateScale({
    drawingDelay,
    scaleFactor = null,
    steps = null,
    origin,
    pan = null,
  }) {
    if (steps === null && scaleFactor === null) {
      throw new Error(
        "Invalid updateScale options: either `steps` or `scaleFactor` must be provided."
      );
    }
    if (!this.pdfDocument) {
      return;
    }
    let newScale = this._currentScale;
    if (scaleFactor > 0 && scaleFactor !== 1) {
      newScale = Math.round(newScale * scaleFactor * 100) / 100;
    } else if (steps) {
      const delta = steps > 0 ? DEFAULT_SCALE_DELTA : 1 / DEFAULT_SCALE_DELTA;
      const round = steps > 0 ? Math.ceil : Math.floor;
      steps = Math.abs(steps);
      do {
        newScale = round((newScale * delta).toFixed(2) * 10) / 10;
      } while (--steps > 0);
    }
    newScale = MathClamp(newScale, MIN_SCALE, MAX_SCALE);

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Always pass exactly one of `steps` (integer, negative shrinks) or `scaleFactor` (positive multiplier != 1) when calling updateScale.
  2. If you only have a target scale, compute it yourself: prefer `pdfViewer.currentScaleValue` / the `#setScale` path, or derive `scaleFactor = targetScale / pdfViewer.currentScale`.
  3. For incremental zoom buttons, call `increaseScale()` / `decreaseScale()` instead of `updateScale` directly — they inject `steps` for you.
  4. When forwarding a user/options bag, default it explicitly: `updateScale({ steps: 1, ...opts })` so a missing key cannot leave both null.

Example fix

// before
pdfViewer.updateScale({ origin: [x, y] });
// after
pdfViewer.updateScale({ scaleFactor: 1.1, origin: [x, y] });
// or, for discrete stepping:
pdfViewer.increaseScale({ origin: [x, y] });
Defensive patterns

Strategy: validation

Validate before calling

// Validate before calling updateScale.
function safeUpdateScale(viewer, opts = {}) {
  const hasFactor = typeof opts.scaleFactor === 'number' && opts.scaleFactor > 0 && opts.scaleFactor !== 1;
  const hasSteps = Number.isInteger(opts.steps);
  if (!hasFactor && !hasSteps) {
    throw new TypeError('updateScale needs `steps` (integer) or `scaleFactor` (>0, !=1)');
  }
  viewer.updateScale(opts);
}

Type guard

/** @param {unknown} o */
function isChangeScaleOptions(o) {
  if (!o || typeof o !== 'object') return false;
  const { scaleFactor, steps } = /** @type {any} */ (o);
  const factorOk = scaleFactor == null || (typeof scaleFactor === 'number' && scaleFactor > 0);
  const stepsOk = steps == null || Number.isInteger(steps);
  return factorOk && stepsOk && (scaleFactor != null || steps != null);
}

Try / catch

try {
  viewer.updateScale(opts);
} catch (e) {
  if (/Invalid updateScale options/.test(e.message)) {
    // degrade: default to one discrete step instead of crashing the UI
    viewer.increaseScale({ origin: opts.origin });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling `pdfViewer.updateScale({})`, `pdfViewer.updateScale({ origin, pan })` (only transform metadata, no zoom delta), or spreading a user-supplied options bag whose `steps`/`scaleFactor` keys are absent/undefined. Also triggered by code that destructures options and forwards a filtered subset that accidentally drops both keys.

Common situations: A custom toolbar or pinch-zoom handler builds a ChangeScaleOptions object dynamically and forwards it without guaranteeing a zoom field; refactoring that removes the `scaleFactor` branch but forgets to set `steps`; passing `scaleFactor: 0` or `scaleFactor: 1` is NOT the trigger (those fall through to the `steps` branch) — the trigger is strictly both being null.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/5cfaf07b765b5a31. Report an issue: GitHub.