dotnet/wpf · error · InvalidOperationException

SR.ParserAssemblyLoadVersionMismatch

Error message

SR.ParserAssemblyLoadVersionMismatch

What it means

LoadAssemblyHelper caches loaded assemblies by short name. When an assembly matching the short name is already cached but its full identity (version/public key) does not satisfy the requested AssemblyName (AssemblyName.ReferenceMatchesDefinition returns false), it throws InvalidOperationException('SR.ParserAssemblyLoadVersionMismatch') showing both the requested and found identity strings. This prevents silently binding a XAML-referenced assembly to a different version than the one the markup expects.

Solutions

  1. Align the requested assembly version with the one actually loaded: update your references/markup to the cached assembly's version.
  2. Call ReflectionHelper.ResetCacheForAssembly(assemblyName) so the stale cache entry is cleared and the correct version can be loaded.
  3. Unify assembly versions across projects (binding redirect or consistent package versions) so only one version exists.
  4. Remove the conflicting older copy from the GAC/probing path, or load the correct version before XAML parsing begins.

Example fix

// before
var asm = ReflectionHelper.LoadAssembly("MyLib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null", null); // cached copy is 1.0.0.0

// after
ReflectionHelper.ResetCacheForAssembly("MyLib");
var asm = ReflectionHelper.LoadAssembly("MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", null);
Defensive patterns

Strategy: try-catch

Validate before calling

// Check for an already-loaded conflicting version before calling LoadAssembly
var requested = new AssemblyName("MyLib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null");
var alreadyLoaded = AppDomain.CurrentDomain.GetAssemblies()
    .FirstOrDefault(a => a.GetName().Name == requested.Name);
if (alreadyLoaded is not null &&
    !AssemblyName.ReferenceMatchesDefinition(requested, alreadyLoaded.GetName()))
    throw new InvalidOperationException(
        $"Version conflict: requested {requested}, loaded {alreadyLoaded.GetName()}");

Type guard

static bool VersionsMatch(AssemblyName request, Assembly loaded) =>
    AssemblyName.ReferenceMatchesDefinition(request, loaded.GetName());

Try / catch

try
{
    var asm = ReflectionHelper.LoadAssembly(assemblyName, assemblyPath);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("version"))
{
    ReflectionHelper.ResetCacheForAssembly(shortName); // clear stale entry and retry once
    var asm = ReflectionHelper.LoadAssembly(assemblyName, assemblyPath);
}

Prevention

When it happens

Trigger: Calling ReflectionHelper.LoadAssembly(assemblyName, assemblyPath) (used by WPF XAML parsing, e.g. LoadBamlAssembly/PresentationOptions) where an assembly with the same short name is already in _loadedAssembliesHash but with a different version than requested in the assemblyName string, ReflectionHelper.cs:416-427.

Common situations: Referencing 'MyAssembly, Version=2.0.0.0' in markup/XmlnsDefinition while version 1.0.0.0 is already loaded in the AppDomain; NuGet or project reference version bumps causing stale GAC/cached copies; mixing debug and release builds of the same assembly name.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/2bc976f5ae0e68c9. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs:425

        private static Assembly LoadAssemblyHelper(string assemblyGivenName, string assemblyPath)
        {
            AssemblyName assemblyName = new AssemblyName(assemblyGivenName);
            string assemblyShortName = assemblyName.Name;
            assemblyShortName = assemblyShortName.ToUpper(CultureInfo.InvariantCulture);

            // Check if the assembly has already been loaded.
            Assembly retassem = (Assembly)_loadedAssembliesHash[assemblyShortName];

            if (retassem is not null)
            {
                if (assemblyName.Version is not null)
                {
                    AssemblyName cachedName = new AssemblyName(retassem.FullName);
                    if (!AssemblyName.ReferenceMatchesDefinition(assemblyName, cachedName))
                    {
                        string request = assemblyName.ToString();
                        string found = cachedName.ToString();
                        throw new InvalidOperationException(SR.Format(SR.ParserAssemblyLoadVersionMismatch, request, found));
                    }
                }
            }
            else
            {
                // Check if the current AppDomain has this assembly loaded for some other reason.
                // If so, then just use that assembly and don't attempt to load another copy of it.
                // Only do this if no path is provided.
                if (string.IsNullOrEmpty(assemblyPath))
                    retassem = SafeSecurityHelper.GetLoadedAssembly(assemblyName);

                if (retassem is null)
                {
                    if (!string.IsNullOrEmpty(assemblyPath))
                    {
                        // assemblyPath is set, Load the assembly from this specified place.
                        // the path must be full file path which contains directory, file name and extension.
                        Debug.Assert(!assemblyPath.EndsWith(string.Empty + Path.DirectorySeparatorChar, StringComparison.Ordinal), "the assembly path should be a full file path containing file extension");

View on GitHub (pinned to 81131a70a4)