Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry.Instrumentation.Wcf/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

* Fixed `ArgumentNullException` thrown by `TelemetryEndpointBehavior` when an endpoint
operation has a `null` Action (e.g., when WCF service help pages are enabled).
([#4026](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4026))

* Updated OpenTelemetry core component version(s) to `1.15.1`.
([#4020](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/4020))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ internal static void ApplyClientBehaviorToClientRuntime(ClientRuntime clientRunt

foreach (var clientOperation in clientRuntime.ClientOperations)
{
if (clientOperation.Action == null)
{
continue;
}

Comment on lines +84 to +88
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new null-check avoids the exception, but skipping clientOperation.Action == null (and dispatchOperation.Action == null) means actionless operations are never added to actionMappings. The runtime instrumentation normalizes missing/empty SOAP action to string.Empty (see ClientChannelInstrumentation.BeforeSendRequest / TelemetryDispatchMessageInspector.AfterReceiveRequest), so spans for these operations will end up without rpc.service/rpc.method metadata. Consider mapping null actions to string.Empty (with collision handling) instead of continue, so actionless endpoints still get meaningful tags.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@martincostello, what do you think about this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure if having an empty action was valid or not.

I can change it if it makes sense, but I figured it at least not throwing an exception and ignoring it was better than the current behaviour.

actionMappings[clientOperation.Action] = new ActionMetadata(
contractName: $"{clientRuntime.ContractNamespace}{clientRuntime.ContractName}",
operationName: clientOperation.Name);
Expand All @@ -96,6 +101,11 @@ internal static void ApplyDispatchBehaviorToEndpoint(EndpointDispatcher endpoint

foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
if (dispatchOperation.Action == null)
{
continue;
}

actionMappings[dispatchOperation.Action] = new ActionMetadata(
contractName: $"{endpointDispatcher.ContractNamespace}{endpointDispatcher.ContractName}",
operationName: dispatchOperation.Name);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Xunit;

namespace OpenTelemetry.Instrumentation.Wcf.Tests;

public class TelemetryEndpointBehaviorTests
{
[Fact]
public void ApplyClientBehaviorToClientRuntime_WithNullActionOperation_DoesNotThrow()
{
// Arrange
var contract = ContractDescription.GetContract(typeof(IServiceContract));
var endpoint = new ServiceEndpoint(contract, new BasicHttpBinding(), new EndpointAddress("http://localhost/dummy"));

endpoint.EndpointBehaviors.Add(new InjectNullActionOperationBehavior());
endpoint.EndpointBehaviors.Add(new TelemetryEndpointBehavior());

var factory = new ChannelFactory<IServiceContract>(endpoint);

try
{
var exception = Record.Exception(factory.Open);
Assert.Null(exception);
}
finally
{
factory.Abort();
}
}
Comment on lines +14 to +35
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test validates the client-side fix, but the reported crash in #2584 occurs in the server-side path (ApplyDispatchBehaviorToEndpoint) when a DispatchOperation.Action is null. Add a .NET Framework-only unit test covering ApplyDispatchBehaviorToEndpoint with a DispatchOperation whose Action is null to ensure the original failure mode stays covered.

Copilot uses AI. Check for mistakes.

private sealed class InjectNullActionOperationBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// No-op
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
var nullActionOp = new ClientOperation(clientRuntime, "NullActionOperation", null);
clientRuntime.ClientOperations.Add(nullActionOp);
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// No-op
}

public void Validate(ServiceEndpoint endpoint)
{
// No-op
}
}
}
Loading