jackc/pgx · error

channel binding required but server does not support SCRAM-S

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.

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)

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: /data/errors/54414192ecfaacc4.json. Report an issue: GitHub.