Skip to content

Include full requesting assembly chain in assembly load failure exceptions#125795

Draft
Copilot wants to merge 14 commits intomainfrom
copilot/fix-issue-9185
Draft

Include full requesting assembly chain in assembly load failure exceptions#125795
Copilot wants to merge 14 commits intomainfrom
copilot/fix-issue-9185

Conversation

Copy link
Contributor

Copilot AI commented Mar 19, 2026

  • Remove inner exception chain walking section from clrex.cpp
  • Delete unrelated file: JsonNamingPolicyAttribute.cs (newly added, not referenced in csproj or ref assembly)
  • Delete unrelated file: Microsoft.Bcl.Cryptography/CompatibilitySuppressions.xml (newly added, unrelated to this PR)
  • Fix failing test: remove grandparent chain assertion (Assert.Contains("MissingDependency.Root"...)) since the implementation provides only the immediate requesting assembly
  • Update comments in FileLoadException.CoreCLR.cs and FileNotFoundException.CoreCLR.cs to say "requesting assembly" instead of "requesting assembly chain"
  • Code review passed
  • Security scan passed (CodeQL skipped due to DB size)

💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI and others added 2 commits March 19, 2026 19:02
When an assembly fails to load, the FileLoadException/FileNotFoundException
now includes the name of the requesting (parent) assembly in the FusionLog
property. This helps diagnose dependency chain issues by showing which
assembly triggered the failed load.

The information flows from the native AssemblySpec::GetParentAssembly()
through EEFileLoadException::CreateThrowable() to the managed exception's
FusionLog property, which is displayed in ToString() output.

Fixes #9185

Co-authored-by: elinor-fung <[email protected]>
BadImageFormatException is also created by EEFileLoadException::CreateThrowable()
when the HRESULT maps to kBadImageFormatException. Without the 3-arg constructor,
the requesting assembly info would be silently dropped for bad image format errors.

Co-authored-by: elinor-fung <[email protected]>
@elinor-fung
Copy link
Member

@copilot we should include the full requesting assembly chain instead of just the parent.

Walk the inner exception chain to build the full dependency path.
When A→B→C→D and D fails, D's FusionLog now shows:

  Requesting assembly: 'C'
  Requesting assembly: 'B'
  Requesting assembly: 'A'

At each Throw(AssemblySpec*) level, the current parent assembly name
is appended to every inner EEFileLoadException's chain. The managed
FormatRequestingAssemblyChain helper formats each newline-separated
name with the "Requesting assembly:" resource string.

Co-authored-by: elinor-fung <[email protected]>
Copy link
Contributor Author

Copilot AI commented Mar 19, 2026

@copilot we should include the full requesting assembly chain instead of just the parent.

Updated in 228155e. Now at each Throw(AssemblySpec*) level, the current parent assembly name is appended to every inner EEFileLoadException's chain. For a dependency chain A→B→C→D where D is missing, D's FusionLog will show:

Requesting assembly: 'C, Version=...'
Requesting assembly: 'B, Version=...'
Requesting assembly: 'A, Version=...'

The managed FormatRequestingAssemblyChain helper (shared by all three exception types) formats each newline-separated name from the native chain string.

Copilot AI changed the title Include requesting assembly in assembly load failure exceptions Include full requesting assembly chain in assembly load failure exceptions Mar 19, 2026
…dd tests

The requesting assembly chain is now appended to the exception's Message
property instead of FusionLog. This means users see the chain directly
in the exception message when it is thrown, without needing to inspect
FusionLog separately.

Added tests for all three exception types (FileLoadException,
FileNotFoundException, BadImageFormatException) that verify:
- Single requesting assembly appears in Message
- Multiple requesting assemblies (chain) appear in Message
- FusionLog remains null

Co-authored-by: elinor-fung <[email protected]>
…missing transitive dependency

Remove the 6 reflection-based tests that invoked private constructors via
reflection from BadImageFormatExceptionTests, FileLoadExceptionTests, and
FileNotFoundExceptionTests.

Add an integration test in System.Runtime.Loader.Tests that creates a real
dependency chain (Root -> Mid -> Leaf) where the transitive dependency
(Leaf) is missing at runtime. The test verifies that a FileNotFoundException
is thrown with the correct missing assembly name when the chain is exercised.

Three helper assembly projects are added:
- MissingDependency.Leaf (not deployed via PrivateAssets=all)
- MissingDependency.Mid (depends on Leaf)
- MissingDependency.Root (depends on Mid)

Co-authored-by: elinor-fung <[email protected]>
The test now asserts that the FileNotFoundException message contains
"MissingDependency.Mid" (the requesting assembly), verifying that the
entire dependency chain is reported in the exception message, not just
the missing assembly file name.

Co-authored-by: elinor-fung <[email protected]>
The test now asserts that both MissingDependency.Mid and
MissingDependency.Root appear in the exception message, verifying
the entire dependency chain (Root → Mid → Leaf) is reported.

Co-authored-by: elinor-fung <[email protected]>
Copilot AI and others added 2 commits March 20, 2026 18:23
…nsitive root

The runtime correctly includes the immediate requesting assembly (Mid)
in the FileNotFoundException message, but does not include the full
transitive chain (Root). The chain-building code in clrex.cpp only
propagates when there are nested EEFileLoadException inner exceptions,
which doesn't apply in this single-level missing dependency scenario.

Co-authored-by: elinor-fung <[email protected]>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/aefe2c66-6ab2-450f-a995-01336b6c0328
The test was asserting that the exception message contains both
"MissingDependency.Mid" and "MissingDependency.Root". However, the
runtime only includes the immediate requesting assembly (Mid) in the
FileNotFoundException message - not the full transitive chain.

The chain-building code in clrex.cpp:1735-1762 only propagates when
nested EEFileLoadException inner exceptions exist, which doesn't apply
in this single-level missing dependency scenario (Leaf fails to load,
requested by Mid).

Verified: the test now passes with the rebuilt runtime.

Co-authored-by: elinor-fung <[email protected]>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/aefe2c66-6ab2-450f-a995-01336b6c0328
}

// Do not delete: this is invoked from native code.
// Used when the requesting assembly chain is known, to provide assembly load dependency context.
Copy link
Member

Choose a reason for hiding this comment

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

This talks about chains, but the implementation seems to be only providing the immediate requesting assembly (no chain).

…::Throw

The dependency chain comes from the binding cache walk, not from inner
exceptions. An exception is only thrown for the missing dependency itself,
so walking inner exceptions to propagate requesting assembly chains is
unnecessary.

Co-authored-by: elinor-fung <[email protected]>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/c54f4451-c7fd-4071-a6fc-57dbfb164795
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.

3 participants