Skip to content

AssertionError in ERR_TLS_CERT_ALTNAME_INVALID handler when pipelining > 1 #5355

@sydneycollins-cio

Description

@sydneycollins-cio

Bug Description

When using a Pool or Client with pipelining > 1, a TLS connect error of type ERR_TLS_CERT_ALTNAME_INVALID causes an uncaught AssertionError that crashes the process.

Reproduction

import { Pool } from 'undici'

const pool = new Pool('https://wrong.host.example.com', {
  pipelining: 10,
  connections: 5,
})

// Send concurrent requests — crash occurs when TLS handshake fails
await Promise.allSettled(
  Array.from({ length: 20 }, () =>
    pool.request({ path: '/', method: 'GET' })
  )
)

Error:

AssertionError [ERR_ASSERTION]: false == true
    at connect (undici/lib/client.js:1313:7)

Root Cause

In lib/client.js, the ERR_TLS_CERT_ALTNAME_INVALID connect error handler contains:

if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
  assert(client[kRunning] === 0)  // crashes when pipelining > 1
  while (client[kPending] > 0 ...) { ... }
}

With pipelining > 1, multiple requests can be in-flight (kRunning > 0) when the TLS error fires at connect time. The assertion assumes pipelining=1 and throws when that assumption is violated.

Every other error code routes through onError() which already handles kRunning > 0 correctly. The ERR_TLS_CERT_ALTNAME_INVALID path is the only one with this broken assumption.

Fix

Drain in-flight running requests before draining pending ones, mirroring the existing onError pattern:

if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') {
  while (client[kRunning] > 0) {
    const request = client[kQueue][client[kRunningIdx]]
    client[kQueue][client[kRunningIdx]++] = null
    errorRequest(client, request, err)
  }
  client[kPendingIdx] = client[kRunningIdx]
  while (client[kPending] > 0 ...) { ... }
}

Versions

  • undici: 5.29.0 (same assertion exists in v6 lib/dispatcher/client.js)
  • node: 20.x
  • Confirmed: crash does not occur with pipelining: 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions