Fix race condition in OpenAI client validation causing NullReferenceException #12248
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
The test
RejectsConnectionStringWithBothModelAndDeploymentwas failing intermittently with aNullReferenceExceptioninstead of the expectedInvalidOperationException. The test validates that connection strings containing bothDeploymentandModelkeys are properly rejected with a meaningful error message.Stack trace showed the error occurring in
AzureEventSourceListener.OnEventSourceCreatedduring Azure client creation, before the validation logic could execute.Root Cause
In both
CreateInnerChatClientandCreateInnerEmbeddingGeneratormethods, the order of operations was:GetRequiredDeploymentName()When the Azure client factory attempted to create a client with an invalid configuration (both
DeploymentandModelkeys present), it would fail during initialization before the validation code could run, resulting in aNullReferenceExceptioninstead of the expectedInvalidOperationException.Solution
Reordered operations to validate the deployment name before attempting to get the OpenAI client:
This ensures connection string validation happens first, throwing the proper
InvalidOperationExceptionwith a clear error message before any client creation is attempted.Testing
Aspire.Azure.AI.OpenAI.TestspassAspire.OpenAI.TestspassRejectsConnectionStringWithBothModelAndDeploymenttest for both chat client and embedding generatorFiles Changed
src/Components/Aspire.OpenAI/AspireOpenAIClientBuilderChatClientExtensions.cssrc/Components/Aspire.OpenAI/AspireOpenAIClientBuilderEmbeddingGeneratorExtensions.csWarning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
api.openai.com/usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Aspire.OpenAI.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Aspire.OpenAI.Tests.deps.json /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Microsoft.DotNet.RemoteExecutor.dll Aspire.OpenAI.Tests, Version=42.42.42.42, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 Aspire.OpenAI.Tests.ConformanceTests <TracingEnablesTheRightActivitySource_Keyed>b__18_0 /tmp/vjqrvfdr.ooc(dns block)/usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net8.0/Aspire.OpenAI.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net8.0/Aspire.OpenAI.Tests.deps.json /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net8.0/Microsoft.DotNet.RemoteExecutor.dll Aspire.OpenAI.Tests, Version=42.42.42.42, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 Aspire.OpenAI.Tests.ConformanceTests <TracingEnablesTheRightActivitySource_Keyed>b__18_0 /tmp/41vnxmy0.kiq(dns block)/usr/share/dotnet/dotnet exec --runtimeconfig /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Aspire.OpenAI.Tests.runtimeconfig.json --depsfile /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Aspire.OpenAI.Tests.deps.json /home/REDACTED/work/aspire/aspire/artifacts/bin/Aspire.OpenAI.Tests/Debug/net9.0/Microsoft.DotNet.RemoteExecutor.dll Aspire.OpenAI.Tests, Version=42.42.42.42, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 Aspire.OpenAI.Tests.ConformanceTests <TracingEnablesTheRightActivitySource>b__17_0 /tmp/i0yuwwxt.0al(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
Investigate this failing test:
🔴 Aspire.Azure.AI.OpenAI.Tests.AspireAzureOpenAIClientBuilderChatClientExtensionsTests.RejectsConnectionStringWithBothModelAndDeployment(useKeyed: True)
Assert.Throws() Failure: Exception type was not an exact match
Expected: typeof(System.InvalidOperationException)
Actual: typeof(System.NullReferenceException)
---- System.NullReferenceException : Object reference not set to an instance of an object. at Aspire.Azure.AI.OpenAI.Tests.AspireAzureOpenAIClientBuilderChatClientExtensionsTests.RejectsConnectionStringWithBothModelAndDeployment(Boolean useKeyed) in //tests/Aspire.Azure.AI.OpenAI.Tests/AspireAzureOpenAIClientBuilderChatClientExtensionsTests.cs:line 122
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span
1 copyOfArgs, BindingFlags invokeAttr) ----- Inner Stack Trace ----- at Azure.Core.Diagnostics.AzureEventSourceListener.OnEventSourceCreated(EventSource eventSource) at Azure.Core.Diagnostics.AzureEventSourceListener..ctor(Action1 log, EventLevel level)at Microsoft.Extensions.Azure.AzureEventSourceLogForwarder.Start()
at Microsoft.Extensions.Azure.AzureClientFactory
2.CreateClient(String name) at Microsoft.Extensions.Azure.AzureClientFactoryBuilder.<>c__152.b__15_1(IServiceProvider provider)at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument)at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
at System.Collections.Concurrent.ConcurrentDictionary
2.GetOrAdd(TKey key, Func2 valueFactory)at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.Extensions.Hosting.AspireAzureOpenAIExtensions.<>c.b__1_0(IServiceProvider provider) in //src/Components/Aspire.Azure.AI.OpenAI/AspireAzureOpenAIExtensions.cs:line 50
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument)at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
at System.Collections.Concurrent.ConcurrentDictionary
2.GetOrAdd(TKey key, Func2 valueFactory)at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.Extensions.Hosting.AspireOpenAIClientBuilderChatClientExtensions.CreateInnerChatClient(IServiceProvider services, AspireOpenAIClientBuilder builder, String deploymentName) in //src/Components/Aspire.OpenAI/AspireOpenAIClientBuilderChatClientExtensions.cs:line 63
at Microsoft.Extensions.Hosting.AspireOpenAIClientBuilderChatClientExtensions.<>c__DisplayClass1_0.b__0(IServiceProvider services) in //src/Components/Aspire.OpenAI/AspireOpenAIClientBuilderChatClientExtensions.cs:line 50
at Microsoft.Extensions.AI.ChatClientBuilder.Build(IServiceProvider servic...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.