denoland/deno · error · Error

ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS

ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS

Error message

Number of custom settings exceeds MAX_ADDITIONAL_SETTINGS

What it means

The customSettings map in HTTP/2 settings (http2.connect(url, { settings }), http2.createServer({ settings }), session.updateSettings) carries non-standard SETTINGS identifiers. validateSettings counts ObjectEntries(settings.customSettings) and throws ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS when there are more than MAX_ADDITIONAL_SETTINGS, which is 10 (ext/node/polyfills/internal/http2/util.ts:217). The transport layer only reserves room for ten extra entries.

Source

Thrown at ext/node/polyfills/http2.ts:1370

// 1. headerTableSize must be a number in the range 0 <= n <= kMaxInt
// 2. initialWindowSize must be a number in the range 0 <= n <= kMaxInitWindowSize
// 3. maxFrameSize must be a number in the range 16384 <= n <= kMaxFrameSize
// 4. maxConcurrentStreams must be a number in the range 0 <= n <= kMaxStreams
// 5. maxHeaderListSize must be a number in the range 0 <= n <= kMaxInt
// 6. enablePush must be a boolean
// 7. enableConnectProtocol must be a boolean
// All settings are optional and may be left undefined
const validateSettings = hideStackFrames((settings) => {
  if (settings === undefined) return;
  assertIsObject(
    settings.customSettings,
    "customSettings",
    "Number",
  );
  if (settings.customSettings) {
    const entries = ObjectEntries(settings.customSettings);
    if (entries.length > MAX_ADDITIONAL_SETTINGS) {
      throw new ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS();
    }
    for (const { 0: key, 1: value } of new SafeArrayIterator(entries)) {
      assertWithinRange(
        "customSettings:id",
        Number(key),
        0,
        0xffff,
      );
      assertWithinRange(
        "customSettings:value",
        Number(value),
        0,
        kMaxInt,
      );
    }
  }

  assertWithinRange(

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Trim customSettings to at most 10 entries and keep only the ids you actually need
  2. Use the standard named settings (headerTableSize, initialWindowSize, maxFrameSize, etc.) where possible instead of custom ids
  3. Remember ids must be integers in 0..0xffff and values in 0..0xffffffff, which are validated right after this check

Example fix

// before
const customSettings = Object.fromEntries(
  Array.from({ length: 16 }, (_, i) => [0x100 + i, 1]),
);
http2.connect(url, { settings: { customSettings } }); // throws

// after
const customSettings = { 0x100: 1, 0x101: 2 }; // at most 10 entries
http2.connect(url, { settings: { customSettings } });
Defensive patterns

Strategy: validation

Validate before calling

const MAX_CUSTOM_SETTINGS = 10;
const entries = Object.entries(settings.customSettings ?? {});
if (entries.length > MAX_CUSTOM_SETTINGS) {
  throw new RangeError(`customSettings exceeds ${MAX_CUSTOM_SETTINGS} entries`);
}
http2.connect(url, { settings });

Type guard

const isBoundedCustomSettings = (
  v: unknown,
): v is Record<number, number> => {
  if (v == null) return true;
  const entries = Object.entries(v as object);
  return entries.length <= 10 && entries.every(
    ([k, val]) =>
      Number(k) >= 0 && Number(k) <= 0xffff &&
      Number(val) >= 0 && Number(val) <= 0xffffffff,
  );
};

Prevention

When it happens

Trigger: Passing a customSettings object with 11 or more keys to http2.connect or http2.createServer; generating customSettings programmatically from a table of experimental settings ids; merging multiple config sources until the map exceeds ten entries.

Common situations: Protocol-experimentation code that enumerates many unknown SETTINGS ids; config merging that accumulates custom settings across environments; copying another peer's full settings dump (which may include many custom ids) into your client config.

Related errors


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