From a user error report (CritterWatch monitoring a Wolverine 6.24.0 service on JasperFx 2.36.2):
System.Reflection.TargetInvocationException:
at JasperFx.Descriptors.OptionsValue.Read (JasperFx/Descriptors/OptionsValue.cs:53)
at JasperFx.Descriptors.OptionsDescription.readProperties (JasperFx/Descriptors/OptionsDescription.cs:156)
at Wolverine.Transports.TransportBase`1.TryBuildBrokerUsage (Wolverine/Transports/TransportBase.cs:22)
at Wolverine.Configuration.Capabilities.ServiceCapabilities.readTransports (ServiceCapabilities.cs:456)
at Wolverine.Configuration.Capabilities.ServiceCapabilities+<ReadFrom>d__74.MoveNext (ServiceCapabilities.cs:161)
at Wolverine.CritterWatch.CritterWatchObserver+<processBatch>d__27.MoveNext (CritterWatchObserver.cs:144)
Inner exception System.NullReferenceException handled at System.Reflection.RuntimeMethodInfo.Invoke:
at Wolverine.AzureServiceBus.AzureServiceBusTransport.get_HostName (AzureServiceBusTransport.cs:280)
Root cause
AzureServiceBusTransport.HostName parses the host out of the connection string via ConnectionString!.Split(';'). For any credential-based connection — FullyQualifiedNamespace plus a TokenCredential / AzureNamedKeyCredential / AzureSasCredential, i.e. the normal managed-identity setup — there is no connection string at all, so the getter throws a NullReferenceException.
HostName is a public property, and BrokerDescription (via JasperFx's OptionsDescription) reads every public property reflectively, so this fires while building the ServiceCapabilities diagnostic snapshot. Two knock-on effects:
- The public
BusClient / ManagementClient properties are also read reflectively, which lazily constructs a live Azure client purely as a side effect of describing configuration — and throws ArgumentNullException when nothing is configured yet.
PulsarTransport's this[Uri] indexer has the same problem from a different direction: PropertyInfo.GetValue(subject) on an indexer throws TargetParameterCountException, so applications using Pulsar can't build a snapshot either.
Impact
ServiceCapabilities.ReadFrom is all-or-nothing, so one bad getter loses the entire snapshot. CritterWatch retries on every batch and hits the same wall, so the service heartbeats forever without ever completing a handshake — no endpoints, no message types, no stores, nothing.
Fix
HostName returns the parsed connection-string host when there is one, otherwise falls back to FullyQualifiedNamespace (which is the host name for credential-based connections), and is null only when nothing at all is configured. Also fixes the IMassTransitInteropEndpoint.MassTransitUri() implementations, which interpolate HostName.
[IgnoreDescription] on BusClient / ManagementClient (live runtime objects, not configuration — NATS and Pulsar already do this) and on the Pulsar indexer.
- Harden
ServiceCapabilities.ReadFrom: each section, plus each individual transport and endpoint, is now best effort — a failure is logged and skipped instead of aborting the snapshot. Cancellation still propagates.
Companion hardening in JasperFx so a throwing getter degrades to a "could not be read" value rather than an exception: JasperFx/jasperfx#590
From a user error report (CritterWatch monitoring a Wolverine 6.24.0 service on JasperFx 2.36.2):
Root cause
AzureServiceBusTransport.HostNameparses the host out of the connection string viaConnectionString!.Split(';'). For any credential-based connection —FullyQualifiedNamespaceplus aTokenCredential/AzureNamedKeyCredential/AzureSasCredential, i.e. the normal managed-identity setup — there is no connection string at all, so the getter throws aNullReferenceException.HostNameis a public property, andBrokerDescription(via JasperFx'sOptionsDescription) reads every public property reflectively, so this fires while building theServiceCapabilitiesdiagnostic snapshot. Two knock-on effects:BusClient/ManagementClientproperties are also read reflectively, which lazily constructs a live Azure client purely as a side effect of describing configuration — and throwsArgumentNullExceptionwhen nothing is configured yet.PulsarTransport'sthis[Uri]indexer has the same problem from a different direction:PropertyInfo.GetValue(subject)on an indexer throwsTargetParameterCountException, so applications using Pulsar can't build a snapshot either.Impact
ServiceCapabilities.ReadFromis all-or-nothing, so one bad getter loses the entire snapshot. CritterWatch retries on every batch and hits the same wall, so the service heartbeats forever without ever completing a handshake — no endpoints, no message types, no stores, nothing.Fix
HostNamereturns the parsed connection-string host when there is one, otherwise falls back toFullyQualifiedNamespace(which is the host name for credential-based connections), and is null only when nothing at all is configured. Also fixes theIMassTransitInteropEndpoint.MassTransitUri()implementations, which interpolateHostName.[IgnoreDescription]onBusClient/ManagementClient(live runtime objects, not configuration — NATS and Pulsar already do this) and on the Pulsar indexer.ServiceCapabilities.ReadFrom: each section, plus each individual transport and endpoint, is now best effort — a failure is logged and skipped instead of aborting the snapshot. Cancellation still propagates.Companion hardening in JasperFx so a throwing getter degrades to a "could not be read" value rather than an exception: JasperFx/jasperfx#590