denoland/deno · error · RangeError

Expected highWaterMark to be a positive number or Infinity,

Error message

Expected highWaterMark to be a positive number or Infinity, got "${highWaterMark}".

What it means

extractHighWaterMark validates the highWaterMark field of any queuingStrategy passed to ReadableStream, WritableStream, and TransformStream constructors (ext/web/06_streams.js:720-731): undefined falls back to the constructor's default; NaN or any negative number throws RangeError 'Expected highWaterMark to be a positive number or Infinity, got ...'. Both 0 and Infinity are legal. Common triggers are the -1-as-'unlimited' idiom borrowed from other libraries, and NaN from computed or parsed configuration.

Source

Thrown at ext/web/06_streams.js:726

    throw new RangeError(
      "Cannot enqueue value with size: chunk size is invalid",
    );
  }
  container[_queue].enqueueWithSize(value, size);
  container[_queueTotalSize] += size;
}

/**
 * @param {QueuingStrategy} strategy
 * @param {number} defaultHWM
 */
function extractHighWaterMark(strategy, defaultHWM) {
  if (strategy.highWaterMark === undefined) {
    return defaultHWM;
  }
  const highWaterMark = strategy.highWaterMark;
  if (NumberIsNaN(highWaterMark) || highWaterMark < 0) {
    throw new RangeError(
      `Expected highWaterMark to be a positive number or Infinity, got "${highWaterMark}".`,
    );
  }
  return highWaterMark;
}

/**
 * Shared size algorithm for the default queuing strategy. Hot paths compare
 * against it by identity to skip the per-chunk call (and its try/catch).
 * @returns {number}
 */
function defaultSizeAlgorithm() {
  return 1;
}

/**
 * @template T
 * @param {QueuingStrategy<T>} strategy

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Use a non-negative finite number or Infinity — replace the -1 'unlimited' idiom with Infinity.
  2. Validate before constructing: if (Number.isNaN(hwm) || hwm < 0) throw a config error naming the source field.
  3. When unsure, omit highWaterMark and rely on the constructor default.
  4. Sanity-check parsed config: coerce with Number() first and reject NaN explicitly.

Example fix

// before
new WritableStream(sink, { highWaterMark: -1 }); // RangeError

// after
new WritableStream(sink, { highWaterMark: 1 }); // use Infinity for effectively-unbounded
Defensive patterns

Strategy: validation

Validate before calling

const hwm = strategy.highWaterMark;
if (hwm !== undefined && (Number.isNaN(hwm) || hwm < 0)) {
  throw new RangeError(`bad highWaterMark: ${hwm}`);
}
const stream = new WritableStream(sink, { highWaterMark: hwm ?? 1 });

Type guard

const isValidHWM = (n) =>
  n === undefined || (typeof n === 'number' && !Number.isNaN(n) && n >= 0);

Try / catch

try {
  stream = new ReadableStream(src, strategy);
} catch (e) {
  if (e instanceof RangeError && e.message.includes('highWaterMark')) {
    stream = new ReadableStream(src, { highWaterMark: 1 });
  } else throw e;
}

Prevention

When it happens

Trigger: new WritableStream(sink, { highWaterMark: -1 }); new TransformStream(t, { highWaterMark: NaN }); any strategy whose highWaterMark is computed as a negative number (e.g. limit * -1) or parsed from user config into NaN.

Common situations: Config-driven strategies where -1 is used to mean 'no limit' (other ecosystems allow it); NaN sneaking in through parseInt of bad input or undefined arithmetic; strategies copy-pasted between stream types with different defaults.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/2e673233109b50d8. Report an issue: GitHub.