denoland/deno · error · RangeError
incomingMaxAge cannot be negative
Error message
incomingMaxAge cannot be negative
What it means
WebTransportDatagramDuplexStream.incomingMaxAge is a writable property (an 'unrestricted double?' in IDL) that caps how old received datagrams may be. Deno's setter in ext/web/webtransport.js rejects negative values and NaN with a RangeError. Zero is normalized to null (no age limit). The check happens in the setter, so even assigning from a computation throws synchronously.
Source
Thrown at ext/web/webtransport.js:803
promise.resolve(undefined);
}
this.#sending = false;
}
get incomingMaxAge() {
webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
return this.#incomingMaxAge;
}
set incomingMaxAge(value) {
webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
value = webidl.converters["unrestricted double?"](
value,
"Failed to execute 'incomingMaxAge' on 'WebTransportDatagramDuplexStream'",
);
if (value < 0 || NumberIsNaN(value)) {
throw new RangeError("incomingMaxAge cannot be negative");
}
if (value === 0) value = null;
this.#incomingMaxAge = value;
}
get outgoingMaxAge() {
webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
return this.#outgoingMaxAge;
}
set outgoingMaxAge(value) {
webidl.assertBranded(this, WebTransportDatagramDuplexStreamPrototype);
value = webidl.converters["unrestricted double?"](
value,
"Failed to execute 'outgoingMaxAge' on 'WebTransportDatagramDuplexStream'",
);
if (value < 0 || NumberIsNaN(value)) {
throw new RangeError("outgoingMaxAge cannot be negative");View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass a non-negative number of milliseconds, or null for no limit.
- Map config sentinels explicitly: negative or missing means null, not -1.
- Guard computed values: Number.isFinite(v) && v >= 0 before assigning.
- Remember 0 is treated as null (disabled), not as 'expire immediately'.
Example fix
// before duplex.incomingMaxAge = config.maxAgeMs ?? -1; // -1 sentinel throws // after duplex.incomingMaxAge = config.maxAgeMs != null && config.maxAgeMs > 0 ? config.maxAgeMs : null;
Defensive patterns
Strategy: validation
Validate before calling
function toMaxAge(v) {
return Number.isFinite(v) && v > 0 ? v : null;
}
duplex.incomingMaxAge = toMaxAge(config.maxAgeMs); Type guard
function isValidMaxAge(v) {
return v == null || (typeof v === "number" && Number.isFinite(v) && v >= 0);
} Try / catch
try { duplex.incomingMaxAge = v; } catch (e) { if (e instanceof RangeError) duplex.incomingMaxAge = null; else throw e; } Prevention
- Map config sentinels (-1/undefined) to null at the boundary.
- Remember 0 is normalized to null, not 'expire now'.
- Validate all numeric datagram tuning with Number.isFinite checks before assignment.
When it happens
Trigger: duplex.incomingMaxAge = -1 or a NaN from a bad calculation like undefined * 1000; reading a config value in milliseconds where a '-1 means unset' convention collides with this API; subtracting a larger baseline from a smaller timestamp.
Common situations: Translating config sentinels (-1 for 'default') from other APIs into WebTransport options; NaN leaking from optional fields ('maxAge: undefined' times a unit factor); unit conversions producing negative values when inputs are misordered.
Related errors
- outgoingMaxAge cannot be negative
- incomingHighWaterMark cannot be negative
- outgoingHighWaterMark cannot be negative
- BenchContext::start() has already been invoked
- ${prefix}Linter plugin name must only contain lowercase lett
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d67f2aeeacd2b5c2.
Report an issue: GitHub.