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
Thrown by MultiMapImpl when the `types` array passed to a multi-mapping query has zero elements. Dapper needs at least one concrete type so it can build a deserializer for the first result set; an empty array gives it nothing to map onto. This guards the non-generic `Query<TReturn>(cnn, sql, Type[] types, ...)` overload (SqlMapper.cs:1569), which is the only public entry point that lets the caller supply the type list directly.
Source
Thrown at Dapper/SqlMapper.cs:1654
finally
{
ownedCommand?.Parameters.Clear();
ownedCommand?.Dispose();
if (wasClosed) cnn!.Close();
}
}
}
private static CommandBehavior GetBehavior(bool close, CommandBehavior @default)
{
return (close ? (@default | CommandBehavior.CloseConnection) : @default) & Settings.AllowedCommandBehaviors;
}
private static IEnumerable<TReturn> MultiMapImpl<TReturn>(this IDbConnection? cnn, CommandDefinition command, Type[] types, Func<object[], TReturn> map, string splitOn, DbDataReader? reader, Identity? identity, bool finalize)
{
if (types.Length < 1)
{
throw new ArgumentException("you must provide at least one type to deserialize");
}
object? param = command.Parameters;
identity ??= new IdentityWithTypes(command.CommandText, command.CommandTypeDirect, cnn!, types[0], param?.GetType(), types);
CacheInfo cinfo = GetCacheInfo(identity, param, command.AddToCache);
IDbCommand? ownedCommand = null;
DbDataReader? ownedReader = null;
bool wasClosed = cnn?.State == ConnectionState.Closed;
try
{
if (reader is null)
{
ownedCommand = command.SetupCommand(cnn!, cinfo.ParamReader);
if (wasClosed) cnn!.Open();
ownedReader = ExecuteReaderWithFlagsFallback(ownedCommand, wasClosed, CommandBehavior.SequentialAccess | CommandBehavior.SingleResult);
reader = ownedReader;
View on GitHub (pinned to 72a54c475f)
Solutions
- Pass at least one Type in the array, e.g. `new[] { typeof(Customer) }`.
- If you do not need multi-mapping, switch to the plain `Query<T>(sql)` overload instead of the `Type[]` overload.
- Guard the caller: if `types.Length == 0`, skip the query or throw a clearer domain-level exception.
- Prefer the strongly-typed generic `Query<TFirst,TSecond,TReturn>(...)` overload so the compiler enforces the type count.
Example fix
// before
var rows = cnn.Query<Result>(sql, new Type[0], objs => (Result)objs[0]);
// after
var rows = cnn.Query<Result>(sql, new[] { typeof(Result) }, objs => (Result)objs[0]); Defensive patterns
Strategy: validation
Validate before calling
if (types is null || types.Length < 1) throw new ArgumentException("Multi-map requires at least one type.", nameof(types));
var rows = cnn.Query<TReturn>(sql, types, map, param); Type guard
static bool HasMapTypes(Type[] types) => types is not null && types.Length > 0;
Prevention
- Prefer the generic Query<TFirst,TSecond,TReturn> overload so the compiler fixes the type count.
- When building Type[] dynamically, assert Length > 0 before the call.
- Unit-test the dynamic type-building path against an empty source list.
When it happens
Trigger: Calling `connection.Query<TReturn>(sql, types: Array.Empty<Type>(), map: arr => ...)` or passing any zero-length `Type[]` as the third argument to that overload. The generic `Query<TFirst,TSecond,...>` overloads never hit this because they always construct a populated type array internally.
Common situations: Dynamically building the `types` array from reflection or user input and forgetting the case where the source list is empty; refactoring away the last mapped type; copy-pasting a multi-map call and deleting the type arguments.
Related errors
- Cannot be null or empty
- An enumerable sequence of parameters (arrays, lists, etc) is
- you must provide at least one type to deserialize
- An item with the same key has already been added
- type
AI-assisted analysis of DapperLib/Dapper@72a54c475f (2026-08-13).
Data as JSON: /api/errors/34b194caf48402fa.
Report an issue: GitHub.