grpc/grpc-go · error
unable to transfer jwtAccess PerRPCCredentials
Error message
unable to transfer jwtAccess PerRPCCredentials: %v
What it means
Returned by jwtAccess.GetRequestMetadata when CheckSecurityLevel finds the transport below PrivacyAndIntegrity. jwtAccess signs a self-signed JWT per RPC and attaches it as a bearer token, so gRPC refuses to send it over an insecure channel. The %v is the security-level error. Trigger and fix mirror errors 215/218/219.
Solutions
- Dial with credentials.NewTLS(&tls.Config{}).
- Use a self-signed cert for local testing instead of insecure.NewCredentials().
- Run the credential on the secure hop when behind a TLS-terminating proxy.
Example fix
// before
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(jwtCreds))
// after
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), grpc.WithPerRPCCredentials(jwtCreds)) Defensive patterns
Strategy: validation
Validate before calling
conn, err := grpc.Dial(addr,
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
grpc.WithPerRPCCredentials(jwtAccessCreds),
) Try / catch
if status.Code(err) == codes.Unauthenticated && strings.Contains(err.Error(), "jwtAccess PerRPCCredentials") {
log.Fatal("jwtAccess credential requires TLS transport")
} Prevention
- Pair NewJWTAccessFromKey/File with TLS transport credentials.
- Use self-signed certs for local testing.
- Add a lint check forbidding insecure.NewCredentials alongside jwtAccess creds.
When it happens
Trigger: Dialing with insecure.NewCredentials() while using oauth.NewJWTAccessFromFile/FromKey as the per-RPC credential; a credentials bundle whose transport negotiates below PrivacyAndIntegrity.
Common situations: Local dev with TLS disabled; service mesh stripping TLS on the hop where the credential runs.
Related errors
- cannot send secure credentials on an insecure connection
- unable to transfer oauthAccess PerRPCCredentials
- unable to transfer serviceAccount PerRPCCredentials
- unable to transfer TokenSource PerRPCCredentials
- credentials: cannot send secure credentials on an insecure…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/9612dec2fafff0d0.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/oauth/oauth.go:107
// Remove RPC service name from URI that will be used as audience
// in a self-signed JWT token. It follows https://google.aip.dev/auth/4111.
aud, err := removeServiceNameFromJWTURI(uri[0])
if err != nil {
return nil, err
}
// TODO: the returned TokenSource is reusable. Store it in a sync.Map, with
// uri as the key, to avoid recreating for every RPC.
ts, err := google.JWTAccessTokenSourceFromJSON(j.jsonKey, aud)
if err != nil {
return nil, err
}
token, err := ts.Token()
if err != nil {
return nil, err
}
ri, _ := credentials.RequestInfoFromContext(ctx)
if err = credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("unable to transfer jwtAccess PerRPCCredentials: %v", err)
}
return map[string]string{
"authorization": token.Type() + " " + token.AccessToken,
}, nil
}
func (j jwtAccess) RequireTransportSecurity() bool {
return true
}
// oauthAccess supplies PerRPCCredentials from a given token.
type oauthAccess struct {
token oauth2.Token
}
// NewOauthAccess constructs the PerRPCCredentials using a given token.
//
// Deprecated: use oauth.TokenSource instead.View on GitHub (pinned to 0c51461d27)