grpc/grpc-go · error

credentials: cannot check peer: missing selected ALPN proper

Error message

credentials: cannot check peer: missing selected ALPN property. %s

What it means

Returned by tlsCreds.ClientHandshake in credentials/tls.go:151 when, after a successful TLS handshake, no ALPN protocol was negotiated (NegotiatedProtocol == "") and envconfig.EnforceALPNEnabled is true (the default). HTTP/2 over TLS requires ALPN, so grpc-go refuses connections to ALPN-less servers. The trailing %s points at issue #434 and the 1.67 enforcement change.

Source

Thrown at credentials/tls.go:151

			return nil, nil, err
		}
	case <-ctx.Done():
		conn.Close()
		return nil, nil, ctx.Err()
	}

	// The negotiated protocol can be either of the following:
	// 1. h2: When the server supports ALPN. Only HTTP/2 can be negotiated since
	//    it is the only protocol advertised by the client during the handshake.
	//    The tls library ensures that the server chooses a protocol advertised
	//    by the client.
	// 2. "" (empty string): If the server doesn't support ALPN. ALPN is a requirement
	//    for using HTTP/2 over TLS. We can terminate the connection immediately.
	np := conn.ConnectionState().NegotiatedProtocol
	if np == "" {
		if envconfig.EnforceALPNEnabled {
			conn.Close()
			return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property. %s", alpnFailureHelpMessage)
		}
		logger.Warningf("Allowing TLS connection to server %q with ALPN disabled. TLS connections to servers with ALPN disabled will be disallowed in future grpc-go releases", cfg.ServerName)
	}
	tlsInfo := TLSInfo{
		State: conn.ConnectionState(),
		CommonAuthInfo: CommonAuthInfo{
			SecurityLevel: PrivacyAndIntegrity,
		},
	}
	id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
	if id != nil {
		tlsInfo.SPIFFEID = id
	}
	return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
}

func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
	conn := tls.Server(rawConn, c.config)

View on GitHub (pinned to 03255a9237)

Solutions

  1. Enable h2 ALPN on the server / TLS terminator (nginx: http2 + ssl_alpn, or grpc ALPN; Go server: rely on grpc's default NextProtos).
  2. As a temporary rollback, set GRPC_ENFORCE_ALPN_ENABLED=false in the client environment (will be removed in a future release).
  3. Bypass the ALPN-stripping proxy for the gRPC port.

Example fix

// before: client env (default)
// GRPC_ENFORCE_ALPN_ENABLED unset -> enforcement on, connection fails

// after (fix the server): nginx
//   listen 443 ssl http2;
//   ssl_alpn h2 http/1.1;

// after (temporary client-side rollback)
//   export GRPC_ENFORCE_ALPN_ENABLED=false
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the server/terminator advertises h2 ALPN before enforcing.
// Client-side mitigation: do nothing if server is ALPN-capable.
// Temporary rollback only for migration:
// os.Setenv("GRPC_ENFORCE_ALPN_ENABLED", "false") // must be set before grpc imports init

Try / catch

if strings.Contains(err.Error(), "missing selected ALPN property") {
    // server lacks ALPN; enable h2 on the server/terminator or set
    // GRPC_ENFORCE_ALPN_ENABLED=false as a temporary rollback
}

Prevention

When it happens

Trigger: Dialing a TLS server (or a proxy/terminator in front of it) that does not support ALPN, while GRPC_ENFORCE_ALPN_ENABLED is true (default). Common after upgrading grpc-go to >=1.67.

Common situations: Post-1.67 upgrade where TLS connections to an older server/proxy stopped working; an HAProxy/nginx/cloud LB that did not enable h2 ALPN; servers configured with TLS but no NextProtos.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/285c741006b4f071. Report an issue: GitHub.