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
- If binding is optional, leave Config.ChannelBinding unset (auto) or set "disable" so auth falls back to plain SCRAM-SHA-256.
- If binding is mandatory, upgrade/reconfigure the server or pooler to advertise SCRAM-SHA-256-PLUS and ensure TLS is end-to-end.
- 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
- Prefer leaving ChannelBinding unset so pgx auto-uses PLUS when available.
- Only set "require" when you control the server and know PLUS is advertised end-to-end.
- Eliminate TLS-terminating proxies that strip the PLUS mechanism.
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
- channel binding required but channel binding data is not ava
- invalid SCRAM ServerSignature received from server
- no peer certificates for channel binding
- invalid SCRAM server-first-message received from server: did
- invalid SCRAM server-first-message received from server: did
AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04).
Data as JSON: /data/errors/54414192ecfaacc4.json.
Report an issue: GitHub.