Skip to content

Improve FileLoadException message for assembly version conflicts with loaded assembly details#123969

Draft
Copilot wants to merge 16 commits intomainfrom
copilot/fix-vague-exception-info
Draft

Improve FileLoadException message for assembly version conflicts with loaded assembly details#123969
Copilot wants to merge 16 commits intomainfrom
copilot/fix-vague-exception-info

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 3, 2026

Description

When Assembly.Load fails due to a version mismatch (requesting v9.0 when v8.0 is loaded), the exception message is uninformative:

Could not load file or assembly 'System.Text.Encoding.CodePages, Version=9.0.0.0, ...'. 
Could not find or load a specific file. (0x80131621)

This change improves the error message to include specific details about both the requested assembly and the already-loaded assembly, including versions and file paths, while maintaining backward compatibility.

Solution

Thread the loaded assembly information from the binder through BindUsingPEImage via an optional out-parameter. When an assembly load fails due to version/MVID mismatch, construct a detailed error message in assemblynative.cpp that includes:

  • The requested assembly name
  • The loaded assembly's full name with version
  • The loaded assembly's file path (when available)

Changes

Modified Files:

  1. src/coreclr/binder/assemblybindercommon.cpp:

    • Modified BindUsingPEImage to accept optional SString* pExistingAssemblyName out-parameter
    • Returns the existing assembly via pExistingAssembly out-parameter when version/MVID conflict detected
  2. src/coreclr/binder/customassemblybinder.cpp & defaultassemblybinder.cpp:

    • Updated BindUsingPEImage signatures to include the new out-parameter
    • Pass through existing assembly information
  3. src/coreclr/binder/bindertracing.cpp:

    • Enhanced tracing output to include loaded assembly version and path for MVID mismatch cases
  4. src/coreclr/vm/assemblybinder.h:

    • Updated BindUsingPEImage signature
  5. src/coreclr/vm/assemblynative.cpp:

    • Retrieve existing assembly details from BindUsingPEImage
    • Construct detailed error messages with loaded assembly name, version, and path
    • Use new resource strings for version and MVID mismatch cases
  6. src/coreclr/dlls/mscorrc/mscorrc.rc & resource.h:

    • Added IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_WITH_VERSION: Error message with loaded assembly version
    • Added IDS_HOST_ASSEMBLY_RESOLVER_ASSEMBLY_ALREADY_LOADED_WITH_VERSION_AND_PATH: Error message with loaded assembly version and file path
  7. Tests:

    • Updated AssemblyResolutionDowngradeTest.cs to validate new error message format
    • Added System.Runtime.Loader.Test.AssemblyVersion2 test project
    • Updated BinderTracingTest.ResolutionFlow.cs to handle new tracing output

Result

Before:

Could not load file or assembly 'System.Text.Encoding.CodePages, Version=9.0.0.0, ...'. 
The located assembly's manifest definition does not match the assembly reference.

After (with path):

Could not load file or assembly 'System.Text.Encoding.CodePages, Version=9.0.0.0, ...'. 
A different version of the assembly 'System.Text.Encoding.CodePages' is already loaded. 
Loaded assembly: 'System.Text.Encoding.CodePages, Version=8.0.0.0, ...' (loaded from '/path/to/assembly.dll')

After (without path):

Could not load file or assembly 'System.Text.Encoding.CodePages, Version=9.0.0.0, ...'. 
A different version of the assembly 'System.Text.Encoding.CodePages' is already loaded. 
Loaded assembly: 'System.Text.Encoding.CodePages, Version=8.0.0.0, ...'

The error message now includes:

  • ✅ The simple name of the assembly
  • ✅ The full name and version of the already-loaded assembly
  • ✅ The file path of the loaded assembly (when available, important for MVID mismatches)

Backward Compatibility

No breaking changes:

  • FileLoadException.HResult returns the same HRESULT values as before
  • Existing code that checks for specific HRESULT values continues to work correctly
  • Only the error message text is improved for clarity
  • The binder's BindUsingPEImage signature change is internal and not exposed via public APIs

Testing

  • ✅ CoreCLR builds successfully
  • ✅ Tests updated to validate new error message format
  • ✅ New test assembly project added for version conflict scenarios
  • ✅ Binder tracing tests updated
  • ✅ No breaking changes to public APIs
Original prompt

This section details on the original issue you should resolve

<issue_title>Vague exception info when lower-versioned assembly is already loaded during Assembly.Load</issue_title>
<issue_description>Sample exception message here:

System.IO.FileLoadException: Could not load file or assembly 'System.Text.Encoding.CodePages, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Could not find or load a specific file. (0x80131621) 

The circumstance is that System.Text.Encoding.CodePages version 8.0 was already loaded, so the request for 9.0 failed. Ideally the message would say this.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 3, 2026
Copilot AI changed the title [WIP] Fix vague exception info for assembly load failures Clarify FUSION_E_REF_DEF_MISMATCH message for assembly version conflicts Feb 3, 2026
Copilot AI requested a review from agocke February 3, 2026 22:21
@agocke
Copy link
Copy Markdown
Member

agocke commented Feb 4, 2026

@copilot this isn't sufficient. We should know precisely what's gone wrong in this case. Let's add a new message specifically for the case that the requested assembly is not compatible with the already-loaded version.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 4, 2026

@copilot this isn't sufficient. We should know precisely what's gone wrong in this case. Let's add a new message specifically for the case that the requested assembly is not compatible with the alread...

Changed the binder to return FUSION_E_APP_DOMAIN_LOCKED for version incompatibility instead of converting it to FUSION_E_REF_DEF_MISMATCH. The error message now precisely states: "The requested assembly version conflicts with what is already bound in the app domain or specified in the manifest." (commit 141b64f)

Copilot AI changed the title Clarify FUSION_E_REF_DEF_MISMATCH message for assembly version conflicts Return FUSION_E_APP_DOMAIN_LOCKED for assembly version conflicts to provide precise error message Feb 4, 2026

// TPA binder returns FUSION_E_REF_DEF_MISMATCH for incompatible version
if (hr == FUSION_E_APP_DOMAIN_LOCKED && isTpaListProvided)
hr = FUSION_E_REF_DEF_MISMATCH;
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.

@copilot Is this HRESULT change observable via public APIs? If it is, it would be an unacceptable breaking change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're correct - this is a breaking change. The HRESULT is observable via FileLoadException.HResult, and code exists that checks for FUSION_E_REF_DEF_MISMATCH (e.g., in Marshal.cs).

I've reverted the change. To provide a precise error message without changing the HRESULT, we would need to either:

  1. Store additional context (like version information) in the exception itself
  2. Enhance the existing FUSION_E_REF_DEF_MISMATCH message, but this is problematic because it's used for both version mismatches and other mismatches (e.g., public key token)

What approach would you recommend?

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.

@copilot Move fixup of FUSION_E_APP_DOMAIN_LOCKED to FUSION_E_REF_DEF_MISMATCH to the point where the HRESULT is converted to an exception. You can attach a custom error message to the exception at the point while keeping the observable HRESULT as FUSION_E_REF_DEF_MISMATCH

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Implemented your suggestion. The conversion from FUSION_E_APP_DOMAIN_LOCKED to FUSION_E_REF_DEF_MISMATCH now happens in EEFileLoadException::Throw() (not the binder), with a custom message attached from the FUSION_E_APP_DOMAIN_LOCKED resource string. The HRESULT remains FUSION_E_REF_DEF_MISMATCH to maintain compatibility. (commits 5a51868, 4da3fd6, bad6db7)

Copilot AI changed the title Return FUSION_E_APP_DOMAIN_LOCKED for assembly version conflicts to provide precise error message [WIP] Improve FileLoadException message for assembly version conflicts Feb 4, 2026
Copilot AI requested a review from jkotas February 4, 2026 03:20
@agocke
Copy link
Copy Markdown
Member

agocke commented Feb 4, 2026

@copilot it sounds like we need to disconnect the production of the error message from the return code. This would allow us to keep the same return codes while producing better error messages.

Copilot AI changed the title [WIP] Improve FileLoadException message for assembly version conflicts Improve FileLoadException message for assembly version conflicts Feb 4, 2026
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 4, 2026

@copilot it sounds like we need to disconnect the production of the error message from the return code. This would allow us to keep the same return codes while producing better error messages.

That's exactly what the current implementation does. The exception returns FUSION_E_REF_DEF_MISMATCH (0x80131040) to maintain compatibility, but the error message is disconnected from this HRESULT - it comes from a custom message stored in m_customMessage that uses the FUSION_E_APP_DOMAIN_LOCKED resource string: "The requested assembly version conflicts with what is already bound in the app domain or specified in the manifest." (commits 5a51868, 4da3fd6, bad6db7)


// Use FUSION_E_APP_DOMAIN_LOCKED's message which specifically describes version conflicts:
// "The requested assembly version conflicts with what is already bound..."
// This provides a clearer error than FUSION_E_REF_DEF_MISMATCH's generic message.
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.

@copilot Please include the full version information in the new message, including the requested version and the currently loaded version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

To include both the requested and loaded versions, I need access to the already-loaded assembly at the exception creation point. Currently, that information is only available in the binder (in the BindResult's AttemptResult) but doesn't flow to EEFileLoadException::Throw().

Would you prefer I:

  1. Add a new Throw overload that accepts the loaded assembly version as a parameter and thread this through the call stack?
  2. Look up the already-loaded assembly in CreateVersionConflictException using AppDomain's assembly cache?
  3. Store the loaded assembly info in a different mechanism (e.g., thread-local storage) that can be accessed when creating the exception?

What approach would be most acceptable?

Copilot AI changed the title Improve FileLoadException message for assembly version conflicts Improve FileLoadException message for assembly version conflicts with loaded assembly details Apr 2, 2026
Copilot AI requested a review from elinor-fung April 2, 2026 00:09
elinor-fung and others added 3 commits April 1, 2026 17:36
Add a new resource string that takes the loaded assembly path as a
separate format argument, producing cleaner messages like:

  Loaded assembly: 'Foo, Version=1.0.0.0' (loaded from 'C:\bar\Foo.dll')

Instead of threading pre-formatted strings, return the conflicting
Assembly* via a new optional out-parameter on BindUsingPEImage. The
caller extracts name and path directly, selecting between the 2-arg
and 3-arg format strings based on path availability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add LoadFromAssemblyPath_MvidMismatch_ErrorMessageIncludesPathAndVersion
to AssemblyLoadContextTest validating that the FileLoadException message
includes the loaded assembly's version and file path.

Also add a v2 test assembly project (same name, version 2.0.0.0) and
update the binder tracing test to check for the path in the error.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Change 'A different version of the assembly' to 'A different copy of
   assembly' in resource strings, since MVID mismatch can occur between
   assemblies with the same version but different content.

2. Include INCLUDE_PUBLIC_KEY_TOKEN in the GetDisplayName call in
   assemblynative.cpp to match bindertracing.cpp, so the exception
   message and ETW trace show consistent assembly identity details.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@elinor-fung elinor-fung force-pushed the copilot/fix-vague-exception-info branch from fba813b to 3380638 Compare April 2, 2026 01:07
@github-actions

This comment has been minimized.

elinor-fung and others added 7 commits April 2, 2026 10:16
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Switch MVID mismatch tests from VersionDowngrade to ReferencedClassLib (a
TPA assembly) and add a ReferencedClassLibVersion2 project for the v2 copy.

Add three test scenarios:
- Custom ALC: load ReferencedClassLib then a different copy
- Default ALC with TPA assembly: ReferencedClassLib is already in TPA,
  load a higher-version copy with different MVID
- Default ALC with non-TPA assembly: load VersionDowngrade v1 then v2
  into Default ALC via RemoteExecutor

Add an optional out parameter to BindAssemblyByNameWorker that returns the
existing assembly from the execution context when the by-name bind fails.
In DefaultAssemblyBinder::BindUsingPEImage, when the TPA version check
fails but an existing assembly is found, perform the MVID comparison
directly and provide the improved error message. Previously, the failure
cache entry from the version check would prevent the MVID check in
AssemblyBinderCommon::BindUsingPEImage from being reached.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d chain

Thread an optional Assembly** out parameter through BindLocked, BindByName,
and BindAssembly so the conflicting assembly is passed out directly at the
point where the version mismatch is detected. This covers both cases:
- Assembly already in the execution context (FindInExecutionContext)
- Assembly freshly loaded from TPA (BindByTpaList)

BindAssemblyByNameWorker now passes through to BindAssembly instead of
doing a redundant ExecutionContext lookup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the FUSION_E_APP_DOMAIN_LOCKED to FUSION_E_REF_DEF_MISMATCH
conversion from the shared BindLocked into DefaultAssemblyBinder's
BindAssemblyByNameWorker, since this is TPA-specific behavior that
does not belong in shared binder code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

elinor-fung and others added 3 commits April 3, 2026 23:19
…mage

Replace the MVID comparison with a check for FUSION_E_REF_DEF_MISMATCH
from BindAssemblyByNameWorker, which already detects the version conflict.
Preserve the original HRESULT in the thrown exception.

In assemblynative.cpp, check for the existing assembly out-param first
to decide whether to show the improved error message, rather than
checking the HRESULT.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use COMPlusThrowHR instead of COMPlusThrow so the original HRESULT
(e.g. FUSION_E_REF_DEF_MISMATCH for TPA version conflicts, COR_E_FILELOAD
for MVID mismatches) is preserved in the managed FileLoadException.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

The parameter is now also used for version conflicts (not just MVID
mismatches), so the name should reflect the broader usage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@elinor-fung
Copy link
Copy Markdown
Member

The old code at defaultassemblybinder.cpp lines ~166-195 (before commit a56bde1) performed an MVID comparison when the TPA by-name bind failed due to a version mismatch.

That check was from a previous change within this PR. It is not in main - the final diff against main here didn't remove any check there.

Assert that COR_E_FILELOAD is preserved for MVID mismatches (custom ALC
and non-TPA Default ALC) and FUSION_E_REF_DEF_MISMATCH is preserved for
TPA version conflicts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@elinor-fung elinor-fung force-pushed the copilot/fix-vague-exception-info branch from be9ffd2 to 3d944dc Compare April 7, 2026 17:30
Copilot AI review requested due to automatic review settings April 7, 2026 17:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves FileLoadException messaging for assembly load failures caused by version/MVID conflicts by including details about the already-loaded assembly (full name/version and, when available, file path).

Changes:

  • Threads an optional “existing assembly on conflict” out-parameter through BindUsingPEImage and related binder helpers.
  • Enhances CoreCLR error message construction (and binder tracing) to include the loaded assembly’s identity and path for conflict scenarios.
  • Updates/adds tests and test assets to validate the new error message content and tracing output.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/coreclr/binder/assemblybindercommon.cpp Threads existing-assembly info through bind helpers and returns it on conflict (notably for MVID mismatch).
src/coreclr/binder/defaultassemblybinder.cpp Propagates existing assembly details for TPA version conflicts so callers can produce richer errors.
src/coreclr/binder/customassemblybinder.cpp Passes through the optional existing-assembly-on-conflict out-parameter.
src/coreclr/binder/bindertracing.cpp Extends tracing to include loaded assembly identity/path for MVID mismatch-related failures.
src/coreclr/vm/assemblybinder.h Updates BindUsingPEImage signature to support returning the conflicting loaded assembly.
src/coreclr/vm/assemblynative.cpp Builds improved FileLoadException messages using existing assembly full name and path when available.
src/coreclr/dlls/mscorrc/resource.h Adds resource IDs for the new detailed “already loaded” messages.
src/coreclr/dlls/mscorrc/mscorrc.rc Adds the new localized message templates used to format detailed conflict info.
src/tests/Loader/binding/tracing/BinderTracingTest.ResolutionFlow.cs Updates tracing test expectations to validate the new error message detail.
src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Tests.csproj Adds shared HResults include and embeds new test assemblies needed for conflict scenarios.
src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Test.AssemblyVersion2/System.Runtime.Loader.Test.AssemblyVersion2.csproj Adds a v2 assembly variant used to provoke version/MVID conflicts in tests.
src/libraries/System.Runtime.Loader/tests/System.Runtime.Loader.Test.AssemblyVersion1/System.Runtime.Loader.Test.AssemblyVersion1.csproj Adjusts versioning properties to align with the new version-conflict test setup.
src/libraries/System.Runtime.Loader/tests/ReferencedClassLibVersion2/ReferencedClassLibVersion2.csproj Adds a v2 referenced class library to create default-ALC TPA vs non-TPA conflict scenarios.
src/libraries/System.Runtime.Loader/tests/ReferencedClassLib/ReferencedClassLib.csproj Sets an explicit v1 AssemblyVersion for baseline comparison in version-mismatch tests.
src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs Adds new tests validating enriched conflict messages and expected HRESULTs across ALC/TPA scenarios.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

🤖 Copilot Code Review — PR #123969

Note

This review was generated by GitHub Copilot.

Holistic Assessment

Motivation: This PR improves diagnostic information for assembly binding conflicts — the existing error messages were vague ("Assembly with same name is already loaded") and the original HRESULT was lost, making it difficult to troubleshoot assembly loading issues. The motivation is clear and well-justified.

Approach: Sound approach — threading the conflicting assembly through the bind chain captures it at the detection point (rather than a separate post-hoc lookup), and using COMPlusThrowHR instead of COMPlusThrow correctly preserves the original HRESULT. Moving the TPA-specific FUSION_E_APP_DOMAIN_LOCKED → FUSION_E_REF_DEF_MISMATCH conversion from shared BindLocked to DefaultAssemblyBinder::BindAssemblyByNameWorker is architecturally correct — this is TPA-specific behavior that doesn't belong in shared binder code.

Summary: ⚠️ Needs Human Review. The code is well-structured and the tests cover three distinct scenarios comprehensively. However, there is a subtle behavioral change (MVID match success path removal) and an HRESULT change for TPA conflicts that warrant human confirmation. These are likely intentional but should be explicitly acknowledged.


Detailed Findings

⚠️ Removed MVID Match Success Path — Behavioral change in DefaultAssemblyBinder

File: src/coreclr/binder/defaultassemblybinder.cpp (commit a56bde1)

The old code in DefaultAssemblyBinder::BindUsingPEImage had a path where, after a by-name bind failed with a version mismatch but pExistingAssembly was set, it compared MVIDs. If MVIDs matched (same assembly binary despite different version binding), it would set *ppAssembly = pExistingAssembly.Extract() and goto Exit — ostensibly returning success.

The new code removes this path entirely, always treating FUSION_E_REF_DEF_MISMATCH as a hard error.

I verified that the old MVID match path was dead code: it set *ppAssembly but never reset hr from FUSION_E_REF_DEF_MISMATCH to S_OK. The caller in assemblynative.cpp checks hr != S_OK first and would always throw, ignoring the set ppAssembly. So removing this path is correct and safe.

However, a human reviewer should confirm this was intentional: was the old code a long-standing bug where the MVID match path was meant to work but hr was never reset? If so, the removal is correct cleanup. If the intent was always to fail, the dead code should have been caught earlier.

⚠️ HRESULT Change for TPA Version Conflicts — Potentially breaking

File: src/coreclr/vm/assemblynative.cpp

TPA version conflicts now produce FUSION_E_REF_DEF_MISMATCH (0x80131040) in the FileLoadException.HResult instead of COR_E_FILELOAD (0x80131621). This happens because:

  1. Old flow: DefaultAssemblyBinder converted the conflict to COR_E_FILELOAD via IF_FAIL_GO(COR_E_FILELOAD), then COMPlusThrow(kFileLoadException, ...) produced a generic HResult
  2. New flow: DefaultAssemblyBinder keeps FUSION_E_REF_DEF_MISMATCH, then COMPlusThrowHR(hr, ...) preserves it

The tests explicitly validate the new HResult values (FUSION_E_REF_DEF_MISMATCH for TPA, COR_E_FILELOAD for MVID mismatches), confirming this is intentional. This is a net improvement — callers get more specific error information. But any downstream code checking ex.HResult == COR_E_FILELOAD for TPA conflicts would no longer match.

✅ Conflict Assembly Threading — Correct

Files: assemblybindercommon.cpp, defaultassemblybinder.cpp, headers

The ppExistingAssemblyOnFailure out-parameter is correctly threaded through the bind chain:

  • In BindLocked: set for any failed hr (both FindInExecutionContext and BindByTpaList paths), with proper AddRef for reference counting
  • Propagated through BindByNameBindAssembly → callers
  • Default value of nullptr on all new parameters ensures backward compatibility for all existing callers

I verified that for DefaultAssemblyBinder, the only failure case that sets ppExistingAssemblyOnFailure in BindLocked produces FUSION_E_APP_DOMAIN_LOCKED (converted to FUSION_E_REF_DEF_MISMATCH), so the condition hr == FUSION_E_REF_DEF_MISMATCH && pExistingAssembly != nullptr in BindUsingPEImage is correct and covers all cases.

✅ Error Handling Reorder in assemblynative.cpp — Correct

File: src/coreclr/vm/assemblynative.cpp

The check order changed from hr == COR_E_FILELOAD first to pExistingAssembly != nullptr first. This is correct because:

  • For TPA conflicts: hr = FUSION_E_REF_DEF_MISMATCH, pExistingAssembly != nullptr → hits first branch (detailed message)
  • For MVID mismatches: hr = COR_E_FILELOAD, pExistingAssembly != nullptr → hits first branch (detailed message)
  • For COR_E_FILELOAD without conflict assembly: pExistingAssembly == nullptr → hits second branch (generic message)
  • Other errors: falls through to EEFileLoadException::Throw

The presence of pExistingAssembly is a more reliable signal for "we have detailed conflict info" than the HRESULT value.

✅ COMPlusThrowHR Usage — Correct

File: src/coreclr/vm/assemblynative.cpp

The switch from COMPlusThrow(kFileLoadException, IDS_..., name, errorString) to COMPlusThrowHR(hr, IDS_..., name.GetUnicode(), errorString.GetUnicode()) is correct. Note the .GetUnicode() calls — COMPlusThrowHR takes LPCWSTR parameters while COMPlusThrow accepted SString references. Both ultimately create a FileLoadException but COMPlusThrowHR embeds the original HRESULT.

✅ Binder Tracing Enhancement — Good

File: src/coreclr/binder/bindertracing.cpp

The tracing code now produces the same detailed error messages as the exception path (assembly name, version, and path). The null checks are appropriate — inContextAttempt != nullptr && inContextAttempt->AssemblyHolder != nullptr before accessing assembly details, with fallback to the generic message. This ensures trace output matches what users see in exceptions.

✅ Test Coverage — Comprehensive

File: src/libraries/System.Runtime.Loader/tests/AssemblyLoadContextTest.cs

Three distinct scenarios are tested:

Test Scenario Expected HResult
LoadFromAssemblyPath_CustomAlc_VersionMismatch MVID mismatch in custom ALC COR_E_FILELOAD
LoadFromAssemblyPath_DefaultAlc_Tpa_VersionMismatch TPA version conflict FUSION_E_REF_DEF_MISMATCH
LoadFromAssemblyPath_DefaultAlc_NonTpaAssembly_MvidMismatch Non-TPA MVID mismatch (via RemoteExecutor) COR_E_FILELOAD

Each test validates both the error message content (simple name, loaded assembly display name, path) and the specific HResult. The non-TPA test correctly uses RemoteExecutor to isolate the Default ALC state.

The test infrastructure additions (AssemblyVersion2, ReferencedClassLibVersion2) are well-structured — ReferencedClassLibVersion2 shares source with ReferencedClassLib but builds with version 2.0.0.0, and the explicit AssemblyVersion on ReferencedClassLib.csproj ensures a stable version baseline.

💡 CustomAssemblyBinder Now Keeps FUSION_E_APP_DOMAIN_LOCKED — Verify intent

File: src/coreclr/binder/assemblybindercommon.cpp

The old code converted FUSION_E_APP_DOMAIN_LOCKED → FUSION_E_REF_DEF_MISMATCH in shared BindLocked when isTpaListProvided was true. Since custom ALCs don't have TPA lists, this conversion never fired for them. The new code moves it to DefaultAssemblyBinder::BindAssemblyByNameWorker only. For custom ALCs, version mismatches go through AssemblyBinderCommon::BindUsingPEImage's MVID check path instead, which produces COR_E_FILELOAD. This maintains the same effective behavior — just confirming the two paths are functionally equivalent for custom ALCs.

Generated by Code Review for issue #123969 ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Vague exception info when lower-versioned assembly is already loaded during Assembly.Load

5 participants