DapperLib/Dapper · error · ArgumentException
you must provide at least one type to deserialize
Error message
you must provide at least one type to deserialize
What it means
MultiMapAsync (the multi-mapping async path, reached via QueryAsync<TReturn>(sql, Type[] types, map, ...)) throws ArgumentException("you must provide at least one type to deserialize") when the types array has zero elements. Multi-mapping needs at least one source type to split the result set on; an empty array is unrecoverable. Checked after the CommandDefinition is built, before any connection work.
Source
Thrown at Dapper/SqlMapper.Async.cs:979
/// <param name="param">The parameters to use for this query.</param>
/// <param name="transaction">The transaction to use for this query.</param>
/// <param name="buffered">Whether to buffer the results in memory.</param>
/// <param name="splitOn">The field we should split and read the second object from (default: "Id").</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout.</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns>An enumerable of <typeparamref name="TReturn"/>.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Grandfathered")]
public static Task<IEnumerable<TReturn>> QueryAsync<TReturn>(this IDbConnection cnn, string sql, Type[] types, Func<object[], TReturn> map, object? param = null, IDbTransaction? transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
{
var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None, default);
return MultiMapAsync(cnn, command, types, map, splitOn);
}
private static async Task<IEnumerable<TReturn>> MultiMapAsync<TReturn>(this IDbConnection cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn)
{
if (types.Length < 1)
{
throw new ArgumentException("you must provide at least one type to deserialize");
}
object? param = command.Parameters;
var identity = new IdentityWithTypes(command.CommandText, command.CommandTypeDirect, cnn, types[0], param?.GetType(), types);
var info = GetCacheInfo(identity, param, command.AddToCache);
bool wasClosed = cnn.State == ConnectionState.Closed;
try
{
if (wasClosed) await cnn.TryOpenAsync(command.CancellationToken).ConfigureAwait(false);
using var cmd = command.TrySetupAsyncCommand(cnn, info.ParamReader);
using var reader = await ExecuteReaderWithFlagsFallbackAsync(cmd, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult, command.CancellationToken).ConfigureAwait(false);
var results = MultiMapImpl(null, default, types, map, splitOn, reader, identity, true);
return command.Buffered ? results.ToList() : results;
}
finally
{
if (wasClosed) cnn.Close();
}
View on GitHub (pinned to 72a54c475f)
Solutions
- Ensure the types array contains at least the primary type before calling the multi-map overload.
- Validate the dynamically built types array length and throw a clearer upstream error if empty.
Example fix
// before
var types = Array.Empty<Type>();
var rows = await cnn.QueryAsync<Foo>(sql, types, map);
// after
var types = new[] { typeof(A), typeof(B) };
var rows = await cnn.QueryAsync<Foo>(sql, types, map); Defensive patterns
Strategy: validation
Validate before calling
if (types is null || types.Length < 1) throw new ArgumentException("At least one type is required for multi-mapping.");
var rows = await cnn.QueryAsync<TReturn>(sql, types, map); Try / catch
try { await cnn.QueryAsync<TReturn>(sql, types, map); }
catch (ArgumentException ex) when (ex.Message.Contains("at least one type"))
{ /* populate the types array with the primary type and retry */ } Prevention
- Build the types array from a guaranteed-non-empty list (the primary type first).
- Validate dynamically constructed arrays length before the multi-map call.
When it happens
Trigger: Calling the multi-map QueryAsync overload with an empty Type[] types array (e.g. types built dynamically from a list that came back empty).
Common situations: Constructing the types array from config/metadata where the source list was empty; programmatically filtering types down to zero; a refactor that dropped the primary type from the array.
Related errors
- type
- Async operations require use of a DbConnection or an already
- Async operations require use of a DbConnection or an IDbConn
- type
- you must provide at least one type to deserialize
AI-assisted analysis of DapperLib/Dapper@72a54c475f (2026-08-13).
Data as JSON: /api/errors/91f6fce24717bc46.
Report an issue: GitHub.