# rpc error: code = PermissionDenied desc = unauthenticated: request not authenticated

- **ID:** `go/grpc-permission-denied-auth-missing`
- **Domain:** go
- **Category:** auth_error
- **Verification:** ai_generated
- **Fix Rate:** 80%

## Root Cause

The gRPC call lacks required authentication credentials, such as a missing or invalid token.

## Version Compatibility

| Version | Status | Introduced | Deprecated |
|---------|--------|------------|------------|
| 1.0 | active | — | — |
| 1.1 | active | — | — |

## Workarounds

1. **Attach a valid authentication token to the gRPC metadata.** (90% success)
   ```
   token := "valid-jwt-token"
conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure(), grpc.WithPerRPCCredentials(&authCreds{token: token}))
// Define authCreds implementing credentials.PerRPCCredentials
func (c *authCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    return map[string]string{"authorization": "Bearer " + c.token}, nil
}
func (c *authCreds) RequireTransportSecurity() bool { return false }
   ```
2. **Implement an interceptor to add credentials automatically.** (85% success)
   ```
   func authInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
    md := metadata.Pairs("authorization", "Bearer token")
    ctx = metadata.NewOutgoingContext(ctx, md)
    return invoker(ctx, method, req, reply, cc, opts...)
}
conn, err := grpc.Dial("localhost:8080", grpc.WithInsecure(), grpc.WithUnaryInterceptor(authInterceptor))
   ```

## Dead Ends

- **Retry the request without adding any credentials.** — The server will continue to reject unauthenticated requests. (100% fail)
- **Use a hardcoded token that may be expired.** — Expired tokens are also rejected by the server. (90% fail)
