denoland/deno · error · NodeError

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 SETTINGS packer in http2/util.ts allows at most MAX_ADDITIONAL_SETTINGS (10, util.ts:217) custom (non-standard) setting identifiers. When an 11th distinct id would be appended to the settings buffer, ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS is thrown before the frame goes out.

Source

Thrown at ext/node/polyfills/internal/http2/util.ts:471

            default:
              set = false;
              break;
          }
        }
        if (!set) { // not supported
          let i = 0;
          while (i < numCustomSettings) {
            if (
              settingsBuffer[IDX_SETTINGS_FLAGS + 1 + 2 * i + 1] === nsetting
            ) {
              settingsBuffer[IDX_SETTINGS_FLAGS + 1 + 2 * i + 2] = val;
              break;
            }
            i++;
          }
          if (i === numCustomSettings) {
            if (numCustomSettings === MAX_ADDITIONAL_SETTINGS) {
              throw new ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS();
            }

            settingsBuffer[IDX_SETTINGS_FLAGS + 1 + 2 * numCustomSettings + 1] =
              nsetting;
            settingsBuffer[IDX_SETTINGS_FLAGS + 1 + 2 * numCustomSettings + 2] =
              val;
            numCustomSettings++;
          }
        }
      }
    }
  }
  settingsBuffer[IDX_SETTINGS_FLAGS + 1] = numCustomSettings;

  if (typeof settings.headerTableSize === "number") {
    flags |= 1 << IDX_SETTINGS_HEADER_TABLE_SIZE;
    settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE] = settings.headerTableSize;
  }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Trim customSettings to at most 10 distinct ids, keeping the most important first
  2. Prefer standard named settings (headerTableSize, initialWindowSize, maxFrameSize, ...) which do not count toward the custom limit
  3. Centralize settings assembly and enforce the cap with an assertion

Example fix

// before
const custom = { ...tracing, ...abTests, ...clientHints }; // 13 distinct ids -> throw

// after
const custom = Object.fromEntries(
  Object.entries({ ...tracing, ...abTests, ...clientHints }).slice(0, 10),
);
Defensive patterns

Strategy: validation

Validate before calling

const MAX_CUSTOM = 10;
function clampCustomSettings(cs = {}) {
  return Object.fromEntries(Object.entries(cs).slice(0, MAX_CUSTOM));
}
settings.customSettings = clampCustomSettings(mergedCustom);

Try / catch

try {
  session = http2.connect(url, { settings });
} catch (err) {
  if (err.code === "ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS") {
    settings.customSettings = Object.fromEntries(
      Object.entries(settings.customSettings ?? {}).slice(0, 10),
    );
    session = http2.connect(url, { settings });
  } else throw err;
}

Prevention

When it happens

Trigger: Passing settings.customSettings with 11+ distinct numeric keys to http2.createServer() or http2.connect(); merging custom settings from multiple modules (tracing + A/B tests + client hints) into one object that exceeds the cap.

Common situations: Feature-flag-heavy setups tunneling many experimental nghttp2 settings; config systems that deep-merge everyone's customSettings into one blob; code copied between client and server that doubles the id count.

Related errors


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