DapperLib/Dapper · error · NotSupportedException

The first item in a list-expansion cannot be null

Error message

The first item in a list-expansion cannot be null

What it means

Thrown by PackListParameters during `IN @list` expansion when the FIRST element of the supplied sequence is null. Dapper inspects the first non-null item to infer the DbType for the generated parameters (it cannot reliably infer a type from null), so a null-first list is rejected with NotSupportedException. Nulls in later positions are handled, but the leading element must not be null.

Solutions

  1. Filter nulls out of the list before passing it: `ids.Where(x => x.HasValue).Select(x => x.Value)`.
  2. Ensure the first element specifically is non-null (reorder or remove the leading null).
  3. Provide an explicit type by using a DbString/typed list so Dapper does not need to infer from the first item.
  4. If the whole list can be null/empty, branch to a query without the IN clause.

Example fix

// before
var ids = new int?[] { null, 1, 2 };
var rows = cnn.Query<Foo>("select * from Foo where Id in @Ids", new { Ids = ids });
// after
var ids = new int?[] { null, 1, 2 }.Where(x => x.HasValue).Select(x => x.Value).ToList();
var rows = cnn.Query<Foo>("select * from Foo where Id in @Ids", new { Ids = ids });
Defensive patterns

Strategy: validation

Validate before calling

// Remove nulls (especially a leading null) before IN-expansion.
var clean = theList?.Where(x => x is not null).ToList();
if (clean is null || clean.Count == 0) return Array.Empty<Foo>();
var rows = cnn.Query<Foo>("select * from Foo where Id in @Ids", new { Ids = clean });

Type guard

static bool FirstItemNotNull(IEnumerable? list) { if (list is null) return true; var e = list.GetEnumerator(); return !e.MoveNext() || e.Current is not null; }

Prevention

When it happens

Trigger: Passing `new { ids = list }` where `list`'s first element is null, in a `where id in @ids` clause, on a provider whose FeatureSupport.Arrays is false (so Dapper does individual-parameter expansion rather than a TVP). Example: `new[] { null, 1, 2 }`.

Common situations: User-supplied filter lists where an empty/null placeholder was prepended; lists built from optional inputs where the first entry was never set; concatenating a null sentinel onto the front of an id list.

Related errors


AI-assisted analysis of DapperLib/Dapper@72a54c475f (2026-08-13). Data as JSON: /api/errors/f82d9bf96651d73f. Report an issue: GitHub.

Appendix: source

Thrown at Dapper/SqlMapper.cs:2197

                var count = 0;
                bool isString = value is IEnumerable<string>;
                bool isDbString = value is IEnumerable<DbString>;
                DbType? dbType = null;

                int splitAt = SqlMapper.Settings.InListStringSplitCount;
                bool viaSplit = splitAt >= 0
                    && TryStringSplit(ref list, splitAt, namePrefix, command, byPosition);

                if (list is not null && !viaSplit)
                {
                    object? lastValue = null;
                    foreach (var item in list)
                    {
                        if (++count == 1) // first item: fetch some type info
                        {
                            if (item is null)
                            {
                                throw new NotSupportedException("The first item in a list-expansion cannot be null");
                            }
                            if (!isDbString)
                            {
                                dbType = LookupDbType(item.GetType(), "", true, out var handler);
                            }
                        }
                        var nextName = namePrefix + count.ToString();
                        if (isDbString && item is DbString str)
                        {
                            str.AddParameter(command, nextName);
                        }
                        else
                        {
                            var listParam = command.CreateParameter();
                            listParam.ParameterName = nextName;
                            if (isString)
                            {
                                listParam.Size = DbString.DefaultLength;

View on GitHub (pinned to 72a54c475f)