denoland/deno · error · RangeError
ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH
ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH
Error message
Packed settings length must be a multiple of six
What it means
The packed SETTINGS format is a sequence of 6-byte records: a 2-byte big-endian setting ID followed by a 4-byte big-endian value. getUnpackedSettings(buf) throws ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH when buf.length % 6 !== 0 because partial records cannot be decoded. The check runs after type validation, once non-Buffer inputs have been converted via Buffer.from.
Source
Thrown at ext/node/polyfills/http2.ts:5534
]);
function getUnpackedSettings(buf) {
if (
// deno-lint-ignore deno-internal/prefer-primordials
!Buffer.isBuffer(buf) &&
!(ArrayBufferIsView(buf) && !(buf instanceof DataView))
) {
throw new ERR_INVALID_ARG_TYPE("buf", [
"Buffer",
"TypedArray",
], buf);
}
if (!Buffer.isBuffer(buf)) {
// deno-lint-ignore deno-internal/prefer-primordials
buf = Buffer.from(buf);
}
if (buf.length % 6 !== 0) {
throw new ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH();
}
const settings = { __proto__: null };
for (let i = 0; i < buf.length; i += 6) {
const id = buf.readUInt16BE(i);
const value = buf.readUInt32BE(i + 2);
const name = SETTING_ID_TO_NAME.get(id);
if (name !== undefined) {
if (name === "enablePush" || name === "enableConnectProtocol") {
settings[name] = value !== 0;
} else {
settings[name] = value;
if (name === "maxHeaderListSize") {
settings.maxHeaderSize = value;
}
}
} else {
// Unknown setting IDs become custom settingsView on GitHub (pinned to 9ad36f7a2c)
Solutions
- Slice the exact payload: for an HTTP/2 frame, the packed settings are frame.subarray(9, 9 + frameLength) — the 9-byte header (3-byte length, type, flags, 4-byte stream id) is not part of the settings.
- Guard with if (buf.length % 6 !== 0) and drop or log the malformed frame instead of decoding it.
- Generate fixtures with http2.getPackedSettings(settings) — its output is always a multiple of 6 and round-trips through getUnpackedSettings.
Example fix
// before const settings = http2.getUnpackedSettings(frame); // frame includes 9-byte header -> length % 6 !== 0 // after const payloadLength = frame.readUIntBE(0, 3); // 24-bit frame length const payload = frame.subarray(9, 9 + payloadLength); const settings = http2.getUnpackedSettings(payload);
Defensive patterns
Strategy: validation
Validate before calling
function isValidPackedSettings(buf) {
return (Buffer.isBuffer(buf) || ArrayBuffer.isView(buf)) && buf.length % 6 === 0;
}
if (!isValidPackedSettings(payload)) throw new Error('malformed SETTINGS payload');
const settings = http2.getUnpackedSettings(payload); Try / catch
try {
const s = http2.getUnpackedSettings(buf);
} catch (e) {
if (e.code === 'ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH') {
// drop/log the malformed frame, do not decode
} else throw e;
} Prevention
- Never decode a SETTINGS frame including its 9-byte header; packed settings are the payload only.
- Round-trip fixtures through http2.getPackedSettings to guarantee well-formed input.
- Validate length % 6 === 0 before decoding untrusted wire data.
When it happens
Trigger: http2.getUnpackedSettings(buf) with a truncated or over-long SETTINGS payload — any byte length not divisible by 6 (e.g. 7, 11, 17); slicing a SETTINGS frame with wrong offsets, such as including the 9-byte frame header or stopping before the payload ends; concatenating unrelated bytes before decoding.
Common situations: Manual HTTP/2 frame parsing that miscomputes payload boundaries; hand-written test fixtures with arbitrary byte counts; reading a SETTINGS frame but slicing from the start of the frame header instead of the payload.
Related errors
- ERR_HTTP2_INVALID_SETTING_VALUE
- ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS
- ERR_HTTP2_INVALID_SETTING_VALUE
- ERR_HTTP2_PUSH_DISABLED
- ERR_INVALID_HTTP_TOKEN
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/61ed5e63b621ce23.
Report an issue: GitHub.