grpc/grpc-go · error
requires SecurityLevel
Error message
requires SecurityLevel %v; connection has %v
What it means
Returned by credentials.CheckSecurityLevel when the established connection's CommonAuthInfo.SecurityLevel is lower than the level the caller required. SecurityLevel ordering is NoSecurity(1) < IntegrityOnly(2) < PrivacyAndIntegrity(3). Per-RPC credential implementations call CheckSecurityLevel to refuse sending secrets over an inadequately protected transport.
Solutions
- Use a transport credential that provides the required level: TLS (credentials.NewTLS) or ALTS (alts.NewClientCreds/NewServerCreds) which both provide PrivacyAndIntegrity.
- Do not pair token/per-RPC credentials with insecure.NewCredentials(); RequireTransportSecurity()==true exists precisely to prevent this.
- If implementing custom TransportCredentials, set CommonAuthInfo.SecurityLevel accurately in your AuthInfo struct.
- Lower the required level only if you have verified the data sensitivity allows it.
Example fix
// before: token creds over insecure transport -> CheckSecurityLevel fails
import "google.golang.org/grpc/credentials/insecure"
creds, _ := google.NewServiceAccountIdentityCredentials(ctx, aud)
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(creds))
// after: use TLS so the connection reports PrivacyAndIntegrity
tlsCreds := credentials.NewTLS(&tls.Config{})
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(tlsCreds), grpc.WithPerRPCCredentials(creds)) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the transport provides PrivacyAndIntegrity before attaching token creds.
func ensureSecureTransport(creds credentials.TransportCredentials) error {
// ALTS and TLS both report PrivacyAndIntegrity; insecure does not.
// There is no public field to inspect; enforce by policy:
if _, ok := creds.(*credentials.TlsCapableCreds); ok { return nil }
// For ALTS, trust by type/name:
return nil // best practice: never pair per-RPC creds with insecure.NewCredentials()
}
// Stronger runtime check: call CheckSecurityLevel with the connection's AuthInfo
// before sending secrets:
// err := credentials.CheckSecurityLevel(peer.AuthInfo, credentials.PrivacyAndIntegrity) Type guard
func isTransportSecure(info credentials.ProtocolInfo) bool {
// ALTS reports SecurityProtocol="alts"; TLS reports "tls".
return info.SecurityProtocol == "tls" || info.SecurityProtocol == "alts"
} Try / catch
// In GetRequestMetadata-style code, return a clear error instead of leaking tokens.
if err := credentials.CheckSecurityLevel(ai, credentials.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("refusing to send credentials over insecure transport: %w", err)
} Prevention
- Never combine per-RPC credentials with insecure.NewCredentials().
- Always use TLS or ALTS transport credentials when RequireTransportSecurity()==true.
- In custom TransportCredentials, set CommonAuthInfo.SecurityLevel correctly.
- Call credentials.CheckSecurityLevel in your PerRPCCredentials.GetRequestMetadata.
When it happens
Trigger: A PerRPCCredentials implementation (e.g. the GCP service-account-identity credentials at gcp_service_account_identity_credentials.go:129) calls CheckSecurityLevel(ai, PrivacyAndIntegrity) and the connection's AuthInfo reports a lower level. Also surfaces anywhere a user manually calls CheckSecurityLevel with a level above what the current transport provides.
Common situations: Using per-RPC token/OAuth credentials over an insecure channel (grpc.WithInsecure / insecure.NewCredentials) instead of TLS or ALTS; using IntegrityOnly credentials (e.g. certain ALTS configs) when the caller requires PrivacyAndIntegrity; a custom TransportCredentials that fails to set SecurityLevel correctly (leaving it at a low value).
Related errors
- cannot send secure credentials on an insecure connection
- credentials: cannot send secure credentials on an insecure…
- unable to transfer jwtAccess PerRPCCredentials
- unable to transfer oauthAccess PerRPCCredentials
- unable to transfer serviceAccount PerRPCCredentials
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/675f42d49c5a0d1d.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/credentials.go:303
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
// It returns success if 1) the condition is satisfied or 2) AuthInfo struct does not implement GetCommonAuthInfo() method
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
//
// This API is experimental.
func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
type internalInfo interface {
GetCommonAuthInfo() CommonAuthInfo
}
if ai == nil {
return errors.New("AuthInfo is nil")
}
if ci, ok := ai.(internalInfo); ok {
// CommonAuthInfo.SecurityLevel has an invalid value.
if ci.GetCommonAuthInfo().SecurityLevel == InvalidSecurityLevel {
return nil
}
if ci.GetCommonAuthInfo().SecurityLevel < level {
return fmt.Errorf("requires SecurityLevel %v; connection has %v", level, ci.GetCommonAuthInfo().SecurityLevel)
}
}
// The condition is satisfied or AuthInfo struct does not implement GetCommonAuthInfo() method.
return nil
}
// ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz.
//
// This API is experimental.
type ChannelzSecurityInfo interface {
GetSecurityValue() ChannelzSecurityValue
}
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
// and *OtherChannelzSecurityValue.
//View on GitHub (pinned to 0c51461d27)