denoland/deno · error · RangeError

incomingHighWaterMark cannot be negative

Error message

incomingHighWaterMark cannot be negative

What it means

WebTransportDatagramDuplexStream.incomingHighWaterMark controls backpressure for the readable side of datagram reception. Deno's setter in ext/web/webtransport.js accepts an 'unrestricted double' and throws a RangeError for negative numbers or NaN. Values below 1 (including 0 and fractions) are not errors — they are clamped up to 1.

Source

Thrown at ext/web/webtransport.js:839

      throw new RangeError("outgoingMaxAge cannot be negative");
    }
    if (value === 0) value = null;
    this.#outgoingMaxAge = value;
  }

  get incomingHighWaterMark() {
    webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
    return this.#incomingHighWaterMark;
  }

  set incomingHighWaterMark(value) {
    webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
    value = webidl.converters["unrestricted double"](
      value,
      "Failed to execute 'incomingHighWaterMark' on 'WebTransportDatagramDuplexStream'",
    );
    if (value < 0 || NumberIsNaN(value)) {
      throw new RangeError("incomingHighWaterMark cannot be negative");
    }
    if (value < 1) value = 1;
    this.#incomingHighWaterMark = value;
  }

  get outgoingHighWaterMark() {
    webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
    return this.#outgoingHighWaterMark;
  }

  set outgoingHighWaterMark(value) {
    webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
    value = webidl.converters["unrestricted double"](
      value,
      "Failed to execute 'outgoingHighWaterMark' on 'WebTransportDatagramDuplexStream'",
    );
    if (value < 0 || NumberIsNaN(value)) {
      throw new RangeError("outgoingHighWaterMark cannot be negative");

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Assign a positive finite number (bytes/entries as the API defines).
  2. Convert 'unset' sentinels to a sane default like 1 instead of -1.
  3. Validate with Number.isFinite(v) && v >= 0 before assigning.
  4. Note the clamp: anything in [0, 1) becomes 1, so small values are safe.

Example fix

// before
duplex.incomingHighWaterMark = cfg.hwm ?? -1;

// after
duplex.incomingHighWaterMark = cfg.hwm ?? 1;
Defensive patterns

Strategy: validation

Validate before calling

function toHighWaterMark(v, fallback = 1) {
  return Number.isFinite(v) && v >= 1 ? v : fallback;
}
duplex.incomingHighWaterMark = toHighWaterMark(cfg.hwm);

Type guard

function isValidHighWaterMark(v) {
  return typeof v === "number" && Number.isFinite(v) && v >= 0;
}

Try / catch

try { duplex.incomingHighWaterMark = v; } catch (e) { if (e instanceof RangeError) duplex.incomingHighWaterMark = 1; else throw e; }

Prevention

When it happens

Trigger: duplex.incomingHighWaterMark = -5; NaN from an uninitialized variable; configuration piping a '-1 = default' sentinel straight into the setter.

Common situations: Size/buffer tuning exposed in config files; unit conversions that yield negative numbers when inputs are swapped; NaN defaults from destructuring with misspelled keys.

Related errors


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