denoland/deno · error · RangeError
ERR_HTTP2_PING_LENGTH
ERR_HTTP2_PING_LENGTH
Error message
HTTP2 ping payload must be 8 bytes
What it means
HTTP/2 PING frames carry exactly 8 bytes of opaque payload, so session.ping(payload, cb) throws ERR_HTTP2_PING_LENGTH when payload.byteLength !== 8. The check deliberately uses byteLength, not length, so a Uint16Array view with 4 elements (8 bytes) is accepted while a 9-byte Buffer is not, matching Node's native validation.
Source
Thrown at ext/node/polyfills/http2.ts:4197
// been called, the ping callback will be invoked immediately with a ping
// cancelled error and a duration of 0.0.
ping(payload, callback) {
if (this.destroyed) {
throw new ERR_HTTP2_INVALID_SESSION();
}
if (typeof payload === "function") {
callback = payload;
payload = undefined;
}
if (payload) {
validateBuffer(payload, "payload");
}
// Use byteLength rather than length so non-Uint8Array views (e.g. a
// Uint16Array of 4 elements = 8 bytes) are correctly accepted, matching
// Node's Http2Session#ping which validates the underlying byte length.
if (payload && payload.byteLength !== 8) {
throw new ERR_HTTP2_PING_LENGTH();
}
validateFunction(callback, "callback");
// Allocate an HTTP2PING async resource so async_hooks observers see
// init/before/after/destroy for each ping, matching Node's Http2Ping
// AsyncWrap in src/node_http2.cc.
const userCb = pingCallback(callback);
const asyncResource = new AsyncResource("HTTP2PING");
const cb = (ack, duration, ackPayload) => {
try {
asyncResource.runInAsyncScope(
userCb,
this,
ack,
duration,
ackPayload,
);
} finally {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Omit the payload entirely and just call session.ping(callback) — Node fills zeros
- Use crypto.randomBytes(8) or Buffer.alloc(8) for a well-formed payload
- Pre-check payload && payload.byteLength !== 8 and fix or reject before calling
Example fix
// before
session.ping(Buffer.from(JSON.stringify({ t: Date.now() })), cb);
// after
session.ping(cb); // or: session.ping(crypto.randomBytes(8), cb); Defensive patterns
Strategy: validation
Validate before calling
const payload = makePayload(); // user data
if (payload !== undefined && payload.byteLength !== 8) {
throw new RangeError(`ping payload must be 8 bytes, got ${payload.byteLength}`);
}
session.ping(payload, callback); Type guard
function isValidPingPayload(p) {
return p === undefined || (ArrayBuffer.isView(p) && p.byteLength === 8);
} Prevention
- Use crypto.randomBytes(8) or Buffer.alloc(8) — never arbitrary-length serialized data
- Remember the check is byteLength: a Uint16Array(4) view is 8 bytes and valid
- Omitting the payload is always safe
When it happens
Trigger: session.ping(Buffer.from('123456789')) (9 bytes), ping(Buffer.alloc(0)), ping(new Uint8Array(16)), or any TypedArray/Buffer whose underlying byte length is not exactly 8.
Common situations: Using a hex-encoded timestamp string (16 characters = 16 bytes) as the payload; generating 'random' IDs with randomBytes(16) copy-pasted from UUID code; sending an application heartbeat object serialized to JSON of arbitrary length.
Related errors
- ERR_INVALID_ARG_VALUE
- ERR_INVALID_ARG_TYPE
- ERR_HTTP2_INVALID_INFO_STATUS
- ERR_OUT_OF_RANGE
- ERR_INVALID_CHAR
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/af1d6907488a1845.
Report an issue: GitHub.