{"record":{"id":"bb7562fb09cdb1d7","repo":"grpc/grpc-go","slug":"grpc-the-credentials-require-transport-level-secu","errorCode":null,"errorMessage":"grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)","messagePattern":"grpc: the credentials require transport level security \\(use grpc\\.WithTransportCredentials\\(\\) to set\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"clientconn.go","lineNumber":100,"sourceCode":"\tPickFirstBalancerName = pickfirst.Name\n)\n\n// The following errors are returned from Dial and DialContext\nvar (\n\t// errNoTransportSecurity indicates that there is no transport security\n\t// being set for ClientConn. Users should either set one or explicitly\n\t// call WithInsecure DialOption to disable security.\n\terrNoTransportSecurity = errors.New(\"grpc: no transport security set (use grpc.WithTransportCredentials(insecure.NewCredentials()) explicitly or set credentials)\")\n\t// errTransportCredsAndBundle indicates that creds bundle is used together\n\t// with other individual Transport Credentials.\n\terrTransportCredsAndBundle = errors.New(\"grpc: credentials.Bundle may not be used with individual TransportCredentials\")\n\t// errNoTransportCredsInBundle indicated that the configured creds bundle\n\t// returned a transport credentials which was nil.\n\terrNoTransportCredsInBundle = errors.New(\"grpc: credentials.Bundle must return non-nil transport credentials\")\n\t// errTransportCredentialsMissing indicates that users want to transmit\n\t// security information (e.g., OAuth2 token) which requires secure\n\t// connection on an insecure connection.\n\terrTransportCredentialsMissing = errors.New(\"grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)\")\n)\n\nvar (\n\tdisconnectionsMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{\n\t\tName:           \"grpc.subchannel.disconnections\",\n\t\tDescription:    \"EXPERIMENTAL. Number of times the selected subchannel becomes disconnected.\",\n\t\tUnit:           \"{disconnection}\",\n\t\tLabels:         []string{\"grpc.target\"},\n\t\tOptionalLabels: []string{\"grpc.lb.backend_service\", \"grpc.lb.locality\", \"grpc.disconnect_error\"},\n\t\tDefault:        false,\n\t})\n\tconnectionAttemptsSucceededMetric = expstats.RegisterInt64Count(expstats.MetricDescriptor{\n\t\tName:           \"grpc.subchannel.connection_attempts_succeeded\",\n\t\tDescription:    \"EXPERIMENTAL. Number of successful connection attempts.\",\n\t\tUnit:           \"{attempt}\",\n\t\tLabels:         []string{\"grpc.target\"},\n\t\tOptionalLabels: []string{\"grpc.lb.backend_service\", \"grpc.lb.locality\"},\n\t\tDefault:        false,","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/clientconn.go#L82-L118","documentation":"errTransportCredentialsMissing (clientconn.go:97-100) is returned by validateTransportCredentials (clientconn.go:494-499) when transport security is 'insecure' AND a PerRPCCredentials whose RequireTransportSecurity()==true is configured. You cannot send security-sensitive call credentials (e.g. OAuth/JWT) over a plaintext channel.","triggerScenarios":"NewClient is called with insecure.NewCredentials() (or any creds whose Info().SecurityProtocol==\"insecure\") together with WithPerRPCCredentials(creds) where creds.RequireTransportSecurity() returns true. Detected at clientconn.go:494-497.","commonSituations":"Local dev with insecure transport but production-style OAuth token attach; mixing insecure creds with oauth.TokenSource / JWT credentials that flag RequireTransportSecurity; partial migration where TLS was removed but per-RPC creds were not.","solutions":["Use TLS transport (WithTransportCredentials(credentials.NewTLS(...))) so the per-RPC credentials are protected.","If you genuinely want plaintext, remove the RequireTransportSecurity per-RPC credentials or supply per-RPC creds whose RequireTransportSecurity() returns false.","For dev only, wrap your token logic in a credential type that returns false from RequireTransportSecurity() — never do this in production.","Audit WithPerRPCCredentials entries; the check fires on the first one requiring transport security."],"exampleFix":"// before — insecure transport + OAuth creds\ncc, _ := grpc.NewClient(target,\n    grpc.WithTransportCredentials(insecure.NewCredentials()),\n    grpc.WithPerRPCCredentials(oauth.TokenSource{oauth2.TokenSource: ts}))\n// err: grpc: the credentials require transport level security ...\n\n// after — TLS so per-RPC creds are protected\ncc, _ := grpc.NewClient(target,\n    grpc.WithTransportCredentials(credentials.NewTLS(tlsConf)),\n    grpc.WithPerRPCCredentials(oauth.TokenSource{oauth2.TokenSource: ts}))","handlingStrategy":"validation","validationCode":"// Reject per-RPC creds that require TLS when transport is insecure\nfunc safePerRPC(c credentials.PerRPCCredentials, insecure bool) error {\n    if insecure && c.RequireTransportSecurity() {\n        return errors.New(\"per-RPC creds require TLS; refusing insecure transport\")\n    }\n    return nil\n}","typeGuard":"func requiresTLS(c credentials.PerRPCCredentials) bool {\n    return c != nil && c.RequireTransportSecurity()\n}","tryCatchPattern":"cc, err := grpc.NewClient(target, opts...)\nif err != nil && strings.Contains(err.Error(), \"require transport level security\") {\n    // swap insecure for TLS, or drop the requiring per-RPC creds\n}","preventionTips":["Use TLS whenever attaching OAuth/JWT per-RPC credentials.","For local dev, use per-RPC creds with RequireTransportSecurity()==false instead of stripping TLS.","Audit WithPerRPCCredentials entries before going insecure."],"tags":["security","credentials","per-rpc","grpc-go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}