grpc/grpc-go · error
unable to transfer TokenSource PerRPCCredentials
Error message
unable to transfer TokenSource PerRPCCredentials: %v
What it means
Returned by oauth.TokenSource.GetRequestMetadata when CheckSecurityLevel finds the transport is below PrivacyAndIntegrity. TokenSource carries OAuth2 access tokens, so gRPC refuses to send them over an insecure channel. The %v is the security-level error. This is the canonical 'use TLS' error for oauth.TokenSource.
Solutions
- Dial with credentials.NewTLS(&tls.Config{}) or a secure bundle.
- For local testing, generate a self-signed cert and use credentials.NewTLS with the appropriate RootCAs.
- Ensure RequireTransportSecurity()==true is honored end-to-end.
Example fix
// before
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(oauth.TokenSource{ts}))
// after
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), grpc.WithPerRPCCredentials(oauth.TokenSource{ts})) Defensive patterns
Strategy: validation
Validate before calling
conn, err := grpc.Dial(addr,
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: ts}),
) Try / catch
if status.Code(err) == codes.Unauthenticated && strings.Contains(err.Error(), "PerRPCCredentials") {
log.Fatal("oauth.TokenSource requires a TLS-secured transport")
} Prevention
- Always pair oauth.TokenSource with TLS transport credentials.
- Use self-signed certs for local dev.
- Audit dial sites for insecure.NewCredentials() in code paths that register oauth creds.
When it happens
Trigger: Dialing with insecure.NewCredentials() while supplying oauth.TokenSource (or oauth.NewComputeEngine / NewApplicationDefault) as the per-RPC credential; a bundle that downgrades transport security.
Common situations: Quick local testing with plaintext; misconfigured dev environment; proxy terminating TLS.
Related errors
- unable to transfer jwtAccess PerRPCCredentials
- unable to transfer oauthAccess PerRPCCredentials
- unable to transfer serviceAccount PerRPCCredentials
- cannot send secure credentials on an insecure connection
- credentials: cannot send secure credentials on an insecure…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/cc3163aec8a9cca5.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/oauth/oauth.go:48
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
"google.golang.org/grpc/credentials"
)
// TokenSource supplies PerRPCCredentials from an oauth2.TokenSource.
type TokenSource struct {
oauth2.TokenSource
}
// GetRequestMetadata gets the request metadata as a map from a TokenSource.
func (ts TokenSource) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
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 TokenSource PerRPCCredentials: %v", err)
}
return map[string]string{
"authorization": token.Type() + " " + token.AccessToken,
}, nil
}
// RequireTransportSecurity indicates whether the credentials requires transport security.
func (ts TokenSource) RequireTransportSecurity() bool {
return true
}
// removeServiceNameFromJWTURI removes RPC service name from URI.
func removeServiceNameFromJWTURI(uri string) (string, error) {
parsed, err := url.Parse(uri)
if err != nil {
return "", err
}
parsed.Path = "/"View on GitHub (pinned to 0c51461d27)