Skip to content

MSBuild C# custom task throws MissingMethodException #13015

@MarcelVersteeg

Description

@MarcelVersteeg

I have a very strange situation. Currently I am writing a custom MSBuild task in C# that I want to use in my build process. This task references a NuGet package, that calls the HttpClient extension method Task<TValue?> GetFromJsonAsync<TValue>(this HttpClient client, string? requestUri, JsonTypeInfo<TValue> jsonTypeInfo, CancellationToken cancellationToken) that is implemented in the System.Net.Http.Json assembly. My own task is targeted against .NET10 and the NuGet package is targeted against .NET9.

I have added the task to my build in the following way:

<UsingTask AssemblyFile="MyTaskAssembly.dll"
           Runtime="NET"
           TaskName="MyCustomTask" />
<Target Name="MyCustomTarget">
    <MyCustomTask />
</Target>

When I run the task during the build, I get the following exception:

error MSB4018: System.MissingMethodException->
Microsoft.Build.Framework
               .BuildException
               .GenericBuildTransferredException: 
Method not found: 'System.Threading.Tasks.Task`1<!!0> 
System.Net.Http
.Json
.HttpClientJsonExtensions
.GetFromJsonAsync(System.Net.Http.HttpClient, 
                  System.String, 
                  System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<!!0>, 
                  System.Threading.CancellationToken)'.

Now, when I create a console application that references the assembly with my custom task and then execute the task, using the following code:

MyTask task = new MyTask();
task.BuildEngine = new BuildEngine();
task.Execute();

the task succeeds without any runtime exceptions.

To find the cause of this, I tried and investigated the following things:

  • Clone the NuGet package source repository and changed the projects to target .NET10 instead of .NET9 and use my custom NuGet package build. This did not fix the issue and the MissingMethodException is still thrown.
  • Run sysinternals' Process Monitor to see if what DLLs are loaded during the execution of the task during a build. I saw that the file C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll is loaded correctly and loaded this file in ILSpy to see if the method actually exists in that assembly and it does.
  • Compared the signature of the GetFromJsonAsync method in .NET9 and .NET10 and there does not seem to be a difference in signature, even the parameter attribute for the requestUri parameter is the same.
  • Added an event handler to AppDomain.CurrentDomain.AssemblyLoad that just logs what assembly is loaded and the assembly System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 is logged as being loaded (confirming what Process Monitor already indicated).
  • Looked at the IL of the method in the NuGet package to see what method is actually called: call class [System.Runtime]System.Threading.Tasks.Task`1<!!0> [System.Net.Http.Json]System.Net.Http.Json.HttpClientJsonExtensions::GetFromJsonAsync<class NpmRegistry.Wrapper.Models.NpmPackage>(class [System.Net.Http]System.Net.Http.HttpClient, string, class [System.Text.Json]System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<!!0>, valuetype [System.Runtime]System.Threading.CancellationToken). It seems to me that it wants to call the method from the System.Net.Http.Json assembly as expected.
  • Using dotnet-trace I hopefully have some additional information. I ran the tool with the following command line: dotnet-trace collect --clreventlevel 5 --clrevents assemblyloader+loader+exception+codesymbols --process-id <pid of the task> (this is needed because the task is run out of process since it is built with .NET10) while performing a build in Visual Studio.This led to the following events right before the exception is thrown:
Provider Name/Event Name	Text
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, NpmRegistry.Wrapper, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [RequestingAssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, AssemblyLoadContextLoad], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/KnownPathProbed	[ClrInstanceID, 9], [FilePath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [Source, ApplicationAssemblies], [Result, 0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, Default], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, ApplicationAssemblies], [AssemblyLoadContext, Default], [Result, Success], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, DefaultAssemblyLoadContextFallback], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, Success], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop	[ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, NpmRegistry.Wrapper, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [RequestingAssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Success, True], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [Cached, False]
Microsoft-Windows-DotNETRuntime/Loader/AssemblyLoad	[AssemblyID, 2354263771168], [AppDomainID, 2355714450368], [AssemblyFlags, ReadyToRun], [FullyQualifiedAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [BindingID, 0], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/Loader/ModuleLoad	[ModuleID, 140719892123472], [AssemblyID, 2354263771168], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ModuleNativePath, ], [ManagedPdbSignature, 7543d2be-596d-99e2-edcc-08858346c399], [ManagedPdbAge, 1], [ManagedPdbBuildPath, /_/src/runtime/artifacts/obj/System.Net.Http.Json/Release/net10.0/System.Net.Http.Json.pdb], [NativePdbSignature, 43cb33d4-065a-2db8-dfdf-2768c026b1dc], [NativePdbAge, 1], [NativePdbBuildPath, System.Net.Http.Json.ni.pdb], [ModuleILFileName, System.Net.Http.Json.dll]
Microsoft-Windows-DotNETRuntime/Loader/DomainModuleLoad	[ModuleID, 140719892123472], [AssemblyID, 2354263771168], [AppDomainID, 2355714450368], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ModuleNativePath, ], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start	[ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyLoadContext, Default], [RequestingAssemblyLoadContext, Default]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/KnownPathProbed	[ClrInstanceID, 9], [FilePath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [Source, ApplicationAssemblies], [Result, 0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, Default], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted	[ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, ApplicationAssemblies], [AssemblyLoadContext, Default], [Result, Success], [ResultAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop	[ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyLoadContext, Default], [RequestingAssemblyLoadContext, Default], [Success, True], [ResultAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [Cached, False]
Microsoft-Windows-DotNETRuntime/Loader/AssemblyLoad	[AssemblyID, 2354263765792], [AppDomainID, 2355714450368], [AssemblyFlags, ReadyToRun], [FullyQualifiedAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [BindingID, 0], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/Loader/ModuleLoad	[ModuleID, 140719892128080], [AssemblyID, 2354263765792], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ModuleNativePath, ], [ManagedPdbSignature, 598db87f-20be-99e5-3876-4757d3bbc727], [ManagedPdbAge, 1], [ManagedPdbBuildPath, /_/src/runtime/artifacts/obj/System.Text.Json/Release/net10.0/System.Text.Json.pdb], [NativePdbSignature, 36eb7d03-7580-e63a-3c25-1d4e2af43414], [NativePdbAge, 1], [NativePdbBuildPath, System.Text.Json.ni.pdb], [ModuleILFileName, System.Text.Json.dll]
Microsoft-Windows-DotNETRuntime/Loader/DomainModuleLoad	[ModuleID, 140719892128080], [AssemblyID, 2354263765792], [AppDomainID, 2355714450368], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ModuleNativePath, ], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/Exception/Start	[ExceptionType, System.MissingMethodException], [ExceptionMessage, Method not found: 'System.Threading.Tasks.Task`1<!!0> System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsync(System.Net.Http.HttpClient, System.String, System.Text.Json.Serialization.Metadata.JsonTypeInfo`1<!!0>, System.Threading.CancellationToken)'.], [ExceptionEIP, 0x00000000], [ExceptionHRESULT, -2146233069], [ExceptionFlags, CLSCompliant], [ClrInstanceID, 9]

This shows that first the System.Net.Http.Json version 10.0.0.0 is loaded (though version 9.0.0.0 is requested) and then System.Text.Json version 10.0.0.0. According to the Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop events, both assemblies are loaded correctly. And right after loading System.Text.Json the MissingMethodException is thrown. The NuGet package that calls the "missing method" is compiled using .NET9, but in my local copy I also compiled it for .NET10 and that did not resolve this issue.

  • By debugging the task and let the debugger break when the MissingMethodException is thrown, I managed to get the stacktrace:
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<NpmRegistry.Wrapper.NpmRegistryClient.<GetPackageData>d__3>(ref NpmRegistry.Wrapper.NpmRegistryClient.<GetPackageData>d__3 stateMachine) Line 38	C#
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.__Canon>.Start<NpmRegistry.Wrapper.NpmRegistryClient.<GetPackageData>d__3>(ref NpmRegistry.Wrapper.NpmRegistryClient.<GetPackageData>d__3 stateMachine) Line 35	C#
NpmRegistry.Wrapper.dll!NpmRegistry.Wrapper.NpmRegistryClient.GetPackageData(string name, string ns, System.Threading.CancellationToken cancellationToken) Line 37	Unknown
... <left out the rest of the stack trace as that is just where I call this GetPackageData method and the startup methods for the process>

The stateMachine variable is generated by the compiler, because the called method is an async one. I also verified if there is an inner exception in the thrown exception, but that property is null.

I am using Visual Studio version 18.1.1 (latest) and building from the IDE, but it also fails when performing the build using MSBuild on the command line, or when using dotnet build.
The version reported by MSBuild is:

MSBuild version 18.0.5+e22287bf1 for .NET Framework
18.0.5.56406

The version reported by dotnet is:

10.0.101

The JsonTypeInfo is generated using the following code:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace NpmRegistry.Wrapper.Models;

[JsonSerializable(typeof(NpmPackage))]
[JsonSourceGenerationOptions(AllowTrailingCommas = true,
                             GenerationMode = JsonSourceGenerationMode.Default,
                             IgnoreReadOnlyFields = false,
                             IgnoreReadOnlyProperties = false,
                             IncludeFields = false,
                             MaxDepth = 15,
                             NumberHandling = JsonNumberHandling.Strict,
                             PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace,
                             ReadCommentHandling = JsonCommentHandling.Skip,
                             UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement,
                             UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
                             UseStringEnumConverter = true,
                             WriteIndented = false)]
internal sealed partial class ModelsSerializerContext : JsonSerializerContext
{
}

and then passed to the call to GetFromJsonAsync as follows: httpClient.GetFromJsonAsync(url, ModelsSerializerContext.Default.NpmPackage, cancellationToken);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions