jackc/pgx · error

channel binding required but channel binding data is not ava

Error message

channel binding required but channel binding data is not available

What it means

Returned by (*PgConn).scramAuth when Config.ChannelBinding == "require" but sc.channelBindingData is still nil after the TLS branch. Channel binding data is the server certificate hash obtained via getTLSCertificateHash; if the connection is not TLS or the hash could not be derived, requiring binding is impossible, so the client aborts rather than silently authenticating without the protection the caller asked for.

Source

Thrown at pgconn/auth_scram.go:72

	// If we have a TLS connection and channel binding is not disabled, attempt to
	// extract the server certificate hash for tls-server-end-point channel binding.
	if tlsConn, ok := c.conn.(*tls.Conn); ok && c.config.ChannelBinding != "disable" {
		certHash, err := getTLSCertificateHash(tlsConn)
		if err != nil && c.config.ChannelBinding == "require" {
			return fmt.Errorf("channel binding required but failed to get server certificate hash: %w", err)
		}

		// Upgrade to SCRAM-SHA-256-PLUS if we have binding data and the server supports it.
		if certHash != nil && serverHasPlus {
			sc.authMechanism = scramSHA256PlusName
		}

		sc.channelBindingData = certHash
		sc.hasTLS = true
	}

	if c.config.ChannelBinding == "require" && sc.channelBindingData == nil {
		return errors.New("channel binding required but channel binding data is not available")
	}

	// Send client-first-message in a SASLInitialResponse
	saslInitialResponse := &pgproto3.SASLInitialResponse{
		AuthMechanism: sc.authMechanism,
		Data:          sc.clientFirstMessage(),
	}
	c.frontend.Send(saslInitialResponse)
	err = c.flushWithPotentialWriteReadDeadlock()
	if err != nil {
		return err
	}

	// Receive server-first-message payload in an AuthenticationSASLContinue.
	saslContinue, err := c.rxSASLContinue()
	if err != nil {
		return err
	}

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. Enable TLS on the connection (sslmode=require/verify-ca/verify-full) so a server certificate hash can be derived.
  2. If TLS is genuinely unavailable, relax Config.ChannelBinding to "" or "disable".
  3. Verify the server presents a certificate whose signature algorithm is supported by getTLSCertificateHash (RSA/ECDSA SHA-256/384/512).

Example fix

// before: channel binding required over plaintext
cc.ChannelBinding = "require"
// conn string has sslmode=disable

// after: require TLS so binding data is available
cc.ChannelBinding = "require"
// conn string: sslmode=verify-full
Defensive patterns

Strategy: validation

Validate before calling

// Ensure TLS is on before requiring channel binding.
func validateBindingNeedsTLS(channelBinding, sslmode string) error {
    if channelBinding == "require" && (sslmode == "" || sslmode == "disable") {
        return errors.New("ChannelBinding=require needs TLS (sslmode != disable)")
    }
    return nil
}

Try / catch

if err := connect(); err != nil {
    if strings.Contains(err.Error(), "channel binding required but channel binding data is not available") {
        cc.ChannelBinding = "" // relax and retry over the same TLS/non-TLS link
    }
}

Prevention

When it happens

Trigger: Config.ChannelBinding == "require" on a plaintext connection (no TLS), or on a TLS connection where getTLSCertificateHash failed non-fatally (certHash nil and ChannelBinding != "require" path left it nil). Most directly: requiring channel binding over a non-TLS socket.

Common situations: Forgetting sslmode/SSL when ChannelBinding="require"; mismatched expectations where ops set ChannelBinding=require but the deployment uses plaintext internal transport.

Related errors


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /data/errors/f21337a7d4e58ce3.json. Report an issue: GitHub.