ObservationGrpcClientInterceptor throws NPE when NameResolver returns empty authority (original) (raw)

Description

When a custom gRPC NameResolver returns an empty string ("") for getServiceAuthority(), the ObservationGrpcClientInterceptor fails to handle this edge case properly, causing a
NullPointerException during observation context creation.

Reproduction repository: https://github.com/GGGGGHT/grpc-uri-authority-reproduce

Environment

Steps to Reproduce

  1. Create a gRPC channel with a custom NameResolver that returns empty string for getServiceAuthority()
  2. Configure ObservationGrpcClientInterceptor on the channel
  3. Make any gRPC call

Expected Behavior

The interceptor should handle empty authority gracefully without throwing an exception.

Actual Behavior

  java.lang.NullPointerException           
        at java.base/java.util.Objects.requireNonNull(Objects.java:220)
        at io.micrometer.common.ImmutableKeyValue.<init>(ImmutableKeyValue.java:38)
        at io.micrometer.common.KeyValue.of(KeyValue.java:48)                                                                                                                                                
        at io.micrometer.common.KeyValue.of(KeyValue.java:58)
        at io.micrometer.common.docs.KeyName.withValue(KeyName.java:46)                                                                                                                                      
        at io.micrometer.core.instrument.binder.grpc.DefaultGrpcClientObservationConvention.getLowCardinalityKeyValues(DefaultGrpcClientObservationConvention.java:51)                                     
        at io.micrometer.core.instrument.binder.grpc.DefaultGrpcClientObservationConvention.getLowCardinalityKeyValues(DefaultGrpcClientObservationConvention.java:29)                                       
        at io.micrometer.observation.SimpleObservation.stop(SimpleObservation.java:174)
        at io.micrometer.core.instrument.binder.grpc.ObservationGrpcClientCall.handleFailure(ObservationGrpcClientCall.java:72)                                                                              
        at io.micrometer.core.instrument.binder.grpc.ObservationGrpcClientCall.start(ObservationGrpcClientCall.java:47)                                                                                    

Root Cause Chain

  1. NameResolver.getServiceAuthority() returns ""
  2. ObservationGrpcClientInterceptor.interceptCall() executes new URI(null, "", null, null, null) which throws URISyntaxException
  3. The exception is caught silently (lines 85-91 in ObservationGrpcClientInterceptor.java), leaving peerName as null
  4. DefaultGrpcClientObservationConvention.getLowCardinalityKeyValues() calls LowCardinalityKeyNames.PEER_NAME.withValue(context.getPeerName()) with null
  5. ImmutableKeyValue.requireNonNull(value) throws NPE

Suggested Fix

In DefaultGrpcClientObservationConvention.getLowCardinalityKeyValues(), add null check for context.getPeerName() before calling withValue():

KeyValue peerNameKeyValue = context.getPeerName() != null
? LowCardinalityKeyNames.PEER_NAME.withValue(context.getPeerName())
: LowCardinalityKeyNames.PEER_NAME.withValue("none");

Or provide a fallback value when peerName is null.

Additional Context

This issue is related to the gRPC observation interceptor, not the RPC method itself. When authority is empty, the URI construction fails and the peerName remains unset, which then causes the NPE when
building observation key-values.