denoland/deno · error · NodeTypeError

ERR_TLS_PROTOCOL_VERSION_CONFLICT

ERR_TLS_PROTOCOL_VERSION_CONFLICT

Error message

TLS protocol version ${options.minVersion || options.maxVersion} conflicts with secureProtocol secureProtocol

What it means

getProtocolRange resolves the TLS version range from options. If options.secureProtocol is set (the legacy OpenSSL method-string API) and minVersion or maxVersion is also set, the two mechanisms conflict and it throws ERR_TLS_PROTOCOL_VERSION_CONFLICT before the method string is even validated - exactly Node's ordering.

Source

Thrown at ext/node/polyfills/_tls_common.ts:297

    return ArrayPrototypeFilter(
      ArrayPrototypeMap(val, (v: any) => toStringOrUndefined(v)!),
      Boolean,
    );
  }
  return toStringOrUndefined(val);
}

function getProtocolRange(
  options: any,
): { minVersion: string; maxVersion: string } {
  let minVersion = getDefaultMinVersion();
  let maxVersion = getDefaultMaxVersion();

  if (options.secureProtocol) {
    // If secureProtocol is set, minVersion/maxVersion must not also be set.
    // Node raises this conflict before validating the protocol method string.
    if (options.minVersion || options.maxVersion) {
      throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(
        options.minVersion || options.maxVersion,
        "secureProtocol",
      );
    }

    const range = kProtocolMap[options.secureProtocol];
    if (!range) {
      if (
        options.secureProtocol === "SSLv2_method" ||
        options.secureProtocol === "SSLv2_client_method" ||
        options.secureProtocol === "SSLv2_server_method"
      ) {
        throw new ERR_TLS_INVALID_PROTOCOL_METHOD("SSLv2 methods disabled");
      }
      if (
        options.secureProtocol === "SSLv3_method" ||
        options.secureProtocol === "SSLv3_client_method" ||
        options.secureProtocol === "SSLv3_server_method"

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Delete secureProtocol and keep minVersion/maxVersion (the modern API)
  2. Or keep secureProtocol alone and remove minVersion/maxVersion
  3. Normalize merged option objects so the legacy field never survives alongside version pins
  4. Standardize new code on minVersion: 'TLSv1.2' style

Example fix

// before
const opts = {
  secureProtocol: 'TLSv1_2_method',
  minVersion: 'TLSv1.2', // conflict -> throws
};

// after
const opts = { minVersion: 'TLSv1.2', maxVersion: 'TLSv1.3' };
Defensive patterns

Strategy: validation

Validate before calling

function normalizeTlsOptions(o) {
  if (o.secureProtocol && (o.minVersion || o.maxVersion)) {
    delete o.secureProtocol; // modern version knobs win
  }
  return o;
}

Try / catch

try {
  sock = new tls.TLSSocket(s, opts);
} catch (e) {
  if (e.code === 'ERR_TLS_PROTOCOL_VERSION_CONFLICT') {
    const { secureProtocol, ...rest } = opts;
    sock = new tls.TLSSocket(s, rest);
  } else throw e;
}

Prevention

When it happens

Trigger: new tls.TLSSocket(sock, { secureProtocol: 'TLSv1_2_method', minVersion: 'TLSv1.2' }); https.createServer({ secureProtocol: 'SSLv23_method', maxVersion: 'TLSv1.3' }); any options object carrying both knob styles.

Common situations: Migrating secureProtocol-era code and adding modern minVersion tuning without removing the legacy field; merging TLS options from multiple sources (base config + overrides); copy-pasted snippets that mix the two APIs.

Understand the failure class

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/0dd96aea98d01fbc. Report an issue: GitHub.