{"record":{"id":"79407ee5e43f9301","repo":"denoland/deno","slug":"err-tls-alpn-callback-with-protocols","errorCode":"ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS","errorMessage":"The ALPNCallback and ALPNProtocols TLS options are mutually exclusive","messagePattern":"The ALPNCallback and ALPNProtocols TLS options are mutually exclusive","errorType":"error_code","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_tls_wrap.js","lineNumber":1385,"sourceCode":"    return new Server(options, listener);\n  }\n\n  if (typeof options === \"function\") {\n    listener = options;\n    options = kEmptyObject;\n  } else if (options == null || typeof options === \"object\") {\n    options ??= kEmptyObject;\n  } else {\n    throw new ERR_INVALID_ARG_TYPE(\"options\", \"object\", options);\n  }\n\n  this._contexts = [];\n  this.requestCert = options.requestCert === true;\n  this.rejectUnauthorized = options.rejectUnauthorized !== false;\n\n  if (options.ALPNProtocols) {\n    if (options.ALPNCallback) {\n      throw new ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS();\n    }\n    convertALPNProtocols(options.ALPNProtocols, this);\n  }\n\n  if (options.sessionTimeout != null) {\n    validateInt32(\n      options.sessionTimeout,\n      \"options.sessionTimeout\",\n      0,\n    );\n  }\n\n  if (options.ticketKeys != null) {\n    if (!isArrayBufferView(options.ticketKeys)) {\n      throw new ERR_INVALID_ARG_TYPE(\n        \"options.ticketKeys\",\n        [\"Buffer\", \"TypedArray\", \"DataView\"],\n        options.ticketKeys,","sourceCodeStart":1367,"sourceCodeEnd":1403,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_tls_wrap.js#L1367-L1403","documentation":"A TLS server can select the ALPN protocol either statically, by listing options.ALPNProtocols, or dynamically, by consulting options.ALPNCallback per connection. Supplying both is ambiguous, so the Server constructor throws ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS (ext/node/polyfills/_tls_wrap.js:1385) as soon as it sees a truthy ALPNProtocols together with a truthy ALPNCallback.","triggerScenarios":"tls.createServer({ ALPNProtocols: ['h2', 'http/1.1'], ALPNCallback: (versions) => ... }); merging a base config that already sets ALPNProtocols with a layer that adds ALPNCallback; spreading defaults plus overrides without deleting either key.","commonSituations":"HTTP/2 + HTTP/1.1 negotiation setups where one layer adds the protocol list and another adds dynamic selection; config spread/merge utilities (defaults deep) that accumulate both keys; upgrading a library version where the callback moved into user config while the list stayed in shared defaults.","solutions":["Choose one mechanism: delete ALPNProtocols when you provide ALPNCallback, or vice versa","After merging config objects, assert mutual exclusivity before constructing the server","Use ALPNProtocols for a fixed protocol list (common case) and reserve ALPNCallback for per-connection logic such as negotiated-protocol audit logging"],"exampleFix":"// before\nconst server = tls.createServer({\n  ...baseOpts, // contains ALPNProtocols\n  ALPNCallback: pickProto, // also set -> throw\n});\n\n// after\nconst { ALPNProtocols, ...rest } = baseOpts;\nconst server = tls.createServer({ ...rest, ALPNCallback: pickProto });","handlingStrategy":"validation","validationCode":"if (opts.ALPNProtocols && opts.ALPNCallback) delete opts.ALPNProtocols; // choose callback","typeGuard":"function hasAlpnConflict(o) { return Boolean(o?.ALPNProtocols && o?.ALPNCallback); }","tryCatchPattern":"try { tls.createServer(opts); } catch (e) { if (e.code === 'ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS') { const { ALPNCallback, ...rest } = opts; return tls.createServer(rest); } throw e; }","preventionTips":["When merging layered TLS configs, delete the key you are overriding instead of overwriting","Assert ALPN exclusivity in config-loading tests"],"tags":["tls","alpn","config","mutually-exclusive"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}