-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Description
In .NET 10 RC2, the ConfigurationBinder throws an exception when attempting to bind an empty array [] from JSON configuration to a nullable IEnumerable<T>? property that has no default value. Non-empty arrays bind successfully to the same property type.
This appears to be a regression introduced by the .NET 10 fix for empty array handling (see dotnet/runtime#58930 and the breaking change documented at https://learn.microsoft.com/en-us/dotnet/core/compatibility/extensions/10.0/configuration-null-values-preserved). That change was made in Support Null configuration (#116677).
Reproduction Steps
1. Create a configuration class:
public class MyServiceOptions
{
public const string Key = "MyService";
// This property causes the crash when binding empty array
public IEnumerable<int>? ExcludedIds { get; set; }
}2. Add configuration in appsettings.json:
{
"MyService": {
"ExcludedIds": []
}
}3. Register the options in Startup.cs or Program.cs:
services.Configure<MyServiceOptions>(
context.Configuration.GetSection(MyServiceOptions.Key)
);4. Inject and use the options in a service:
public class MyService
{
private readonly MyServiceOptions _options;
public MyService(IOptions<MyServiceOptions> options)
{
_options = options.Value; // CRASH happens here
}
}5. Run the application
Result: Application crashes when attempting to resolve IOptions<MyServiceOptions>.Value with:
System.ArgumentNullException: Value cannot be null. (Parameter 'elementType')
at System.Array.CreateInstance(Type elementType, Int32 length)
at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(...)
Expected behavior
Empty arrays in configuration should bind successfully to nullable IEnumerable<T>? properties, either as:
- An empty collection (e.g.,
Array.Empty<int>()) null
This is consistent with how non-empty arrays are handled and how other configuration values work.
Comparison of Scenarios
| Configuration Value | Property Type | Has Default Value | Result |
|---|---|---|---|
[] |
IEnumerable<int>? |
No | CRASH |
[1] |
IEnumerable<int>? |
No | Works |
[] |
IEnumerable<int>? |
= null |
Works |
[] |
IEnumerable<int> |
= [] |
Works |
[] |
List<int>? |
No | Works |
null |
IEnumerable<int>? |
No | Works |
Actual behavior
The ConfigurationBinder attempts to instantiate IEnumerable<T> directly when encountering an empty array, which fails because IEnumerable<T> is an interface and cannot be instantiated.
Regression?
Works fine in .NET 8, breaks in .NET 10, very likely to be related to this fix: https://learn.microsoft.com/en-us/dotnet/core/compatibility/extensions/10.0/configuration-null-values-preserved).
Known Workarounds
Option 1: Add a default value
public IEnumerable<int>? ExcludedIds { get; set; } = null;
// or
public IEnumerable<int> ExcludedIds { get; set; } = [];Option 2: Use concrete type
public List<int>? ExcludedIds { get; set; }Option 3: Change configuration to null
{
"ExcludedIds": null
}Configuration
- .NET Version: 10.0.100-rc.2.25502.107
- OS: macOS (Darwin 25.0.0), but reproducible across platforms
- Configuration Package: Microsoft.Extensions.Configuration 10.0.0-rc.2.25502.107
- Binder Package: Microsoft.Extensions.Configuration.Binder (via Microsoft.Extensions.Hosting)
Other information
Root Cause Analysis
When the ConfigurationBinder encounters:
- Non-empty array (e.g.,
[1]): Creates a concreteint[]and assigns it to theIEnumerable<int>?property (Works) - Empty array (
[]): Attempts to instantiate the property type (IEnumerable<int>) directly, which fails (Crashes)
The binder appears to take different code paths for empty vs. non-empty arrays, with the empty array path incorrectly attempting to instantiate the abstract interface type.
Additional Context
This issue is particularly problematic because:
- Silent failure pattern: Works fine in .NET 8, breaks in .NET 10
- Inconsistent behavior: Non-empty arrays work, empty arrays don't
- Common pattern: Nullable
IEnumerable<T>?without defaults is a common C# pattern for optional collections - Migration blocker: Requires code changes to properties that previously worked fine
Related Issues
- dotnet/runtime#58930 - IConfiguration with empty array elements
- dotnet/extensions#5858 - Empty array in config json file results in null class property
Suggested Fix
The ConfigurationBinder should handle empty arrays consistently with non-empty arrays by creating a concrete collection type (e.g., Array.Empty<T>()) rather than attempting to instantiate the property's interface type.