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>} strategyView on GitHub (pinned to 9ad36f7a2c)
Solutions
- Use a non-negative finite number or Infinity — replace the -1 'unlimited' idiom with Infinity.
- Validate before constructing: if (Number.isNaN(hwm) || hwm < 0) throw a config error naming the source field.
- When unsure, omit highWaterMark and rely on the constructor default.
- 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
- Use Infinity (never -1) for effectively-unbounded buffering
- Validate config-sourced highWaterMark before constructing streams
- Omit highWaterMark when the default is acceptable
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
- Cannot enqueue value with size: chunk size must be a positiv
- Cannot enqueue value with size: chunk size is invalid
- Response body is already used
- Value must be a positive bigint: received ${value}
- Value must fit in a 64-bit unsigned integer
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/2e673233109b50d8.
Report an issue: GitHub.