jackc/pgx · error

channel binding required but server does not support…

Error message

channel binding required but server does not support SCRAM-SHA-256-PLUS

What it means

Returned by (*PgConn).scramAuth when Config.ChannelBinding == "require" but the server's advertised SASL mechanisms do not include SCRAM-SHA-256-PLUS. The PLUS variant carries tls-server-end-point channel binding (RFC 5929); without it the client refuses to proceed to prevent MITM downgrade attacks.

Solutions

  1. If binding is optional, leave Config.ChannelBinding unset (auto) or set "disable" so auth falls back to plain SCRAM-SHA-256.
  2. If binding is mandatory, upgrade/reconfigure the server or pooler to advertise SCRAM-SHA-256-PLUS and ensure TLS is end-to-end.
  3. Remove any TLS-terminating proxy between client and server so the server's certificate is presented directly.

Example fix

// before
cc.ChannelBinding = "require"

// after (auto: uses PLUS when available, plain SCRAM otherwise)
cc.ChannelBinding = ""
Defensive patterns

Strategy: validation

Validate before calling

// Decide channel binding policy based on what the deployment supports.
func resolveChannelBinding(wantRequire bool, serverSupportsPlus bool) string {
    if wantRequire && !serverSupportsPlus {
        // fall back instead of failing at connect time
        return "" // auto
    }
    if wantRequire {
        return "require"
    }
    return ""
}

Try / catch

err := conn.Ping(ctx)
if err != nil && strings.Contains(err.Error(), "channel binding required but server does not support") {
    cc.ChannelBinding = "" // relax and reconnect
}

Prevention

When it happens

Trigger: Config.ChannelBinding set to "require" while connecting to a server that only advertises SCRAM-SHA-256 (no PLUS). Happens over a TLS connection where the client demands binding but the server doesn't support it, or when an intermediary strips the PLUS advertisement.

Common situations: Hardening a connection with ChannelBinding="require" against an older PostgreSQL (< supports PLUS) or a connection-pooler (PgBouncer in some modes) that doesn't forward PLUS; TLS-terminating proxies that drop the mechanism.

Related errors


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

Appendix: source

Thrown at pgconn/auth_scram.go:51

	"golang.org/x/text/secure/precis"
)

const (
	clientNonceLen      = 18
	scramSHA256Name     = "SCRAM-SHA-256"
	scramSHA256PlusName = "SCRAM-SHA-256-PLUS"
)

// Perform SCRAM authentication.
func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
	sc, err := newScramClient(serverAuthMechanisms, c.config.Password)
	if err != nil {
		return err
	}

	serverHasPlus := slices.Contains(sc.serverAuthMechanisms, scramSHA256PlusName)
	if c.config.ChannelBinding == "require" && !serverHasPlus {
		return errors.New("channel binding required but server does not support SCRAM-SHA-256-PLUS")
	}

	// 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
	}

View on GitHub (pinned to ec1a0befd2)