grpc/grpc-go · error
credentials: cannot send secure credentials on an insecure c
Error message
credentials: cannot send secure credentials on an insecure connection: %v
What it means
In GetRequestMetadata the credential calls CheckSecurityLevel(ri.AuthInfo, PrivacyAndIntegrity) and wraps any failure (gcp_service_account_identity_credentials.go:129-131). Because RequireTransportSecurity() returns true, sending the bearer token over anything below TLS/ALTS is refused. The inner %v is typically the [165] 'requires SecurityLevel' message.
Source
Thrown at credentials/google/gcp_service_account_identity_credentials.go:130
ctx: ctx,
audience: audience,
creds: creds,
backoff: internal.BackoffStrategy,
}, nil
}
// GetRequestMetadata gets the current request metadata, refreshing tokens if
// required. This implementation follows the PerRPCCredentials interface.
//
// It guarantees that only one underlying token fetch will be executed
// concurrently. If a valid token is cached, it is returned immediately. If
// a fetch recently failed, the cached error is returned until the backoff
// interval expires. Otherwise, it initiates a new token fetch or blocks
// waiting for an already-in-progress fetch to complete.
func (c *gcpServiceAccountIdentityCallCreds) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
ri, _ := credentials.RequestInfoFromContext(ctx)
if err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("credentials: cannot send secure credentials on an insecure connection: %v", err)
}
if md, err := c.cachedRequestMetadata(true); md != nil || err != nil {
return md, err
}
c.mu.Lock()
// Now that we have the lock, did someone else finish the fetch while we
// were waiting for the lock?
md, err := c.cachedRequestMetadataLocked(false)
if md != nil || err != nil {
c.mu.Unlock()
return md, err
}
// If no one is fetching, start it.
if c.fetching == nil {
c.fetching = make(chan struct{})View on GitHub (pinned to 03255a9237)
Solutions
- Use TLS or ALTS transport credentials on the channel so the connection is PrivacyAndIntegrity.
- Do not attach these credentials to an insecure dial; drop grpc.WithPerRPCCredentials for plaintext testing.
- Verify RequireTransportSecurity()==true is honored by your channel construction (gRPC refuses insecure+secure-creds only if you let it).
- Confirm the transport credentials' AuthInfo sets SecurityLevel correctly (see [165]).
Example fix
// before
conn, _ := grpc.NewClient(addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithPerRPCCreds(grpcCredentials), // triggers insecure-connection error
)
// after
conn, _ := grpc.NewClient(addr,
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ServerName: addr})),
grpc.WithPerRPCCreds(grpcCredentials),
) Defensive patterns
Strategy: validation
Validate before calling
// Ensure the channel is TLS/ALTS before attaching these creds.
if rpcCreds.RequireTransportSecurity() && transportIsInsecure(dialOpts) {
return errors.New("cannot use secure per-RPC creds over insecure transport")
} Try / catch
md, err := rpcCreds.GetRequestMetadata(ctx)
if err != nil && strings.Contains(err.Error(), "insecure connection") {
// configuration error: switch the channel to TLS/ALTS.
return nil, fmt.Errorf("channel transport must be secured: %w", err)
} Prevention
- Always use TLS or ALTS transport credentials when per-RPC creds require security.
- Do not attach RequireTransportSecurity()=true creds to an insecure dev channel.
- Promote insecure dev channels to TLS before adding per-RPC credentials.
- Verify AuthInfo.SecurityLevel is PrivacyAndIntegrity.
When it happens
Trigger: Dialing a gRPC server with grpc.WithTransportCredentials(insecure.NewCredentials()) (or an integrity-only transport) while attaching these per-RPC credentials. The first RPC triggers GetRequestMetadata, which sees NoSecurity AuthInfo and aborts before sending the token.
Common situations: Dev/staging channel configured plaintext 'just to test', forgetting to flip insecure→TLS when promoting; a custom transport whose AuthInfo.SecurityLevel defaults to zero; or a misconfigured ALTS bundle that fell back to plaintext.
Related errors
- requires SecurityLevel %v; connection has %v
- token file access error
- empty token_exchange_service_uri in options
- required field SubjectTokenPath is not specified
- required field SubjectTokenType is not specified
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/644c014078564dbe.
Report an issue: GitHub.