Skip to content

ConfigurationBinder fails to bind empty array in Net 10.0 #126312

@da1910

Description

@da1910

Description

We have a configuration type which defines an array field, in net8.0 we were able to have an entry in our appsettings.json file which was an empty array and we could safely handle it. It was unhelpful that it arrived as null, but you can pass a null to the constructor and handle it safely.

In net 10 the behaviour has "improved" - see #36510 - specifically #36510 (comment)

Now this is arguably better in some ways, but it's extremely surprising, since a typed config object now cannot safely handle deserialization of this kind of value. Rather than null which passes the type check and can then be caught with a null guard in the constructor, the Binder now throws:

Unhandled exception. System.ArgumentException: Object of type 'System.String' cannot be converted to type 'System.String[]'.
   at System.RuntimeType.CheckValue(Object& value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBaseInvoker.InvokeWithManyArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.ConstructorInfo.Invoke(Object[] parameters)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.CreateInstance(Type type, IConfiguration config, BinderOptions options, ParameterInfo[]& constructorParameters)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.BindInstance(Type type, BindingPoint bindingPoint, IConfiguration config, BinderOptions options, Boolean isParentCollection)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.Get(IConfiguration configuration, Type type, Action`1 configureOptions)
   at Microsoft.Extensions.Configuration.ConfigurationBinder.Get[T](IConfiguration configuration, Action`1 configureOptions)

Reproduction Steps

Run in dotnetfiddle against .NET 8 to see an empty array, and against .NET 10 to see the exception.

using System;
using System.IO;
using Microsoft.Extensions.Configuration;

RunTest("Empty array []",              @"{ ""ArrayField"": [] }");

static void RunTest(string label, string json)
{
    Console.WriteLine($"\n=== {label} ===");
    try
    {
        var config = new ConfigurationBuilder()
            .AddJsonStream(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(json)))
            .Build();

        var options = config.Get<MyOptions>();

        var values = options.ArrayField;
        Console.WriteLine($"  Bound: {values.Length} item(s): [{string.Join(", ", values)}]");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"  FAIL — {ex.GetType().Name}: {ex.Message}");
    }
}

public class MyOptions
{
	public MyOptions(string[] arrayField = null)
	{
		ArrayField = arrayField ?? Array.Empty<string>();
	}
	
	public string[] ArrayField { get; }
}

Expected behavior

Object is deserialized into an empty array with no trouble - specifically null is passed and caught

Actual behavior

ArgumentException: Object of type 'System.String' cannot be converted to type 'System.String[]'.

Regression?

Regression from .net9.0 and .net8.0 (which I was using before)

Known Workarounds

Removing the key entirely works, as does passing [null] or [""] but neither of these are the same object as []

Configuration

Net 10 - 10.0.4
Windows 11 x64

Other information

Most of the fixes in the issue are improvements but this seems like a clear regression, as we can no longer safely type string[] entries and expect that they will be deserialized from valid json inputs. At the least it ought to have a documentation warning to make clear that this is not a valid operation, or it needs fixing properly.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions