{"record":{"id":"e3bddbbc069c2a8a","repo":"grpc/grpc-go","slug":"cannot-send-secure-credentials-on-an-insecure-conn","errorCode":null,"errorMessage":"cannot send secure credentials on an insecure connection: %v","messagePattern":"cannot send secure credentials on an insecure connection: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"credentials/jwt/token_file_call_creds.go","lineNumber":79,"sourceCode":"\t\tfileReader:      &jwtFileReader{tokenFilePath: tokenFilePath},\n\t\tbackoffStrategy: backoff.DefaultExponential,\n\t}\n\n\treturn creds, nil\n}\n\n// GetRequestMetadata gets the current request metadata, refreshing tokens if\n// required. This implementation follows the PerRPCCredentials interface.  The\n// tokens will get automatically refreshed if they are about to expire or if\n// they haven't been loaded successfully yet.\n// If it's not possible to extract a token from the file, UNAVAILABLE is\n// returned.\n// If the token is extracted but invalid, then UNAUTHENTICATED is returned.\n// If errors are encoutered, a backoff is applied before retrying.\nfunc (c *jwtTokenFileCallCreds) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {\n\tri, _ := credentials.RequestInfoFromContext(ctx)\n\tif err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot send secure credentials on an insecure connection: %v\", err)\n\t}\n\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.isTokenValidLocked() {\n\t\tneedsPreemptiveRefresh := time.Until(c.cachedExpiry) < preemptiveRefreshThreshold\n\t\tif needsPreemptiveRefresh && !c.pendingRefresh {\n\t\t\t// Start refresh if not pending (handling the prior RPC may have\n\t\t\t// just spawned a goroutine).\n\t\t\tc.pendingRefresh = true\n\t\t\tgo c.refreshToken()\n\t\t}\n\t\treturn map[string]string{\n\t\t\t\"authorization\": c.cachedAuthHeader,\n\t\t}, nil\n\t}\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/credentials/jwt/token_file_call_creds.go#L61-L97","documentation":"Returned by jwtTokenFileCallCreds.GetRequestMetadata when CheckSecurityLevel finds the connection is below PrivacyAndIntegrity. The JWT bearer token is sensitive, so gRPC will not attach it to an insecure transport. The %v holds the security-level check error. Semantically identical to error 201 but emitted by the JWT-from-file credential.","triggerScenarios":"Dialing with insecure.NewCredentials() while registering a jwt.NewTokenFileCallCredentials per-RPC credential; a bundle that downgrades the transport; TLS termination upstream leaving plaintext on this hop.","commonSituations":"Local dev without TLS; sidecar/mesh stripping TLS; misconfigured credentials bundle where RequireTransportSecurity()==true is contradicted by the transport.","solutions":["Dial with credentials.NewTLS(&tls.Config{}) (or equivalent secure transport).","Use a self-signed cert for local testing rather than insecure.NewCredentials().","If TLS is terminated upstream, ensure the per-RPC credential runs on the secure hop or use mTLS through the proxy."],"exampleFix":"// before\nconn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(jwtCreds))\n// after\nconn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), grpc.WithPerRPCCredentials(jwtCreds))","handlingStrategy":"validation","validationCode":"// Always dial with TLS when using JWT-from-file credentials.\nconn, err := grpc.Dial(addr,\n    grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ServerName: addr})),\n    grpc.WithPerRPCCredentials(jwtCreds),\n)","typeGuard":null,"tryCatchPattern":"if status.Code(err) == codes.Unauthenticated && strings.Contains(err.Error(), \"insecure connection\") {\n    log.Fatal(\"JWT credential requires TLS; do not use insecure.NewCredentials()\")\n}","preventionTips":["Never combine jwt.NewTokenFileCallCredentials with insecure.NewCredentials().","For local testing, use a self-signed TLS transport credential.","Add a CI lint rule forbidding insecure.NewCredentials in packages that register JWT creds."],"tags":["grpc","tls","security","jwt","credentials"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}