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
- Align the requested assembly version with the one actually loaded: update your references/markup to the cached assembly's version.
- Call ReflectionHelper.ResetCacheForAssembly(assemblyName) so the stale cache entry is cleared and the correct version can be loaded.
- Unify assembly versions across projects (binding redirect or consistent package versions) so only one version exists.
- 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
- Keep one consistent version of each assembly across all referenced projects and markup.
- Call ResetCacheForAssembly when re-loading an assembly after a version change in the same AppDomain.
- Avoid hard-coding version-qualified assembly names in XAML/registration unless necessary.
- Check AppDomain.CurrentDomain.GetAssemblies() for short-name conflicts before loading.
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
- SR.Format(SR.UnknownBamlRecord, recordType)
- Animation_ChildMustBeKeyFrame
- Animation_ChildMustBeKeyFrame
- Animation_NoTextChildren
- Animation_NoTextChildren
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)