DapperLib/Dapper · error · InvalidOperationException
The Rest field of a ValueTuple must contain a nested ValueTu
Error message
The Rest field of a ValueTuple must contain a nested ValueTuple of arity 1 or greater.
What it means
Thrown during ValueTuple deserializer IL generation when walking the Rest-chain of a ValueTuple result type and finding that the `Rest` field is not itself a ValueTuple. ValueTuples with more than 7 elements nest the overflow in a `Rest` field that must be another ValueTuple<...> of arity >= 1; if that invariant is broken (a malformed or hand-constructed tuple-like type), Dapper cannot continue the chain and throws InvalidOperationException.
Source
Thrown at Dapper/SqlMapper.cs:3413
for (var i = 0; i < itemFieldCount; i++)
{
languageTupleElementTypes.Add(constructorParameterTypes[i]);
}
if (restField is not null)
{
constructorParameterTypes[constructorParameterTypes.Length - 1] = restField.FieldType;
}
constructors.Add(currentValueTupleType.GetConstructor(constructorParameterTypes)!);
if (restField is null) break;
currentValueTupleType = restField.FieldType;
if (!IsValueTuple(currentValueTupleType))
{
throw new InvalidOperationException("The Rest field of a ValueTuple must contain a nested ValueTuple of arity 1 or greater.");
}
}
var stringEnumLocal = (LocalBuilder?)null;
for (var i = 0; i < languageTupleElementTypes.Count; i++)
{
var targetType = languageTupleElementTypes[i];
if (i < length)
{
LoadReaderValueOrBranchToDBNullLabel(
il,
startBound + i,
ref stringEnumLocal,
valueCopyLocal: null,
reader.GetFieldType(startBound + i),
targetType,
View on GitHub (pinned to 72a54c475f)
Solutions
- Map very wide results onto a named class/record instead of an 8+ element ValueTuple.
- If you use a large tuple, ensure it is a genuine C# tuple so the compiler nests Rest correctly.
- Avoid custom structs that imitate ValueTuple naming; use a real DTO.
- Split the query into multiple smaller mappings.
Example fix
// before (fragile, 8+ element tuple / custom Rest) var rows = cnn.Query<(int a, string b, ..., int h, int i)>(sql); // after public record WideRow(int A, string B, int C, int D, int E, int F, int G, int H, int I); var rows = cnn.Query<WideRow>(sql);
Defensive patterns
Strategy: type-guard
Validate before calling
// Avoid 8+ element tuples for results; map to a named DTO. var rows = cnn.Query<WideRow>(sql);
Type guard
static bool HasValidRestChain(Type? t) { while (t is not null && t.FullName?.StartsWith("System.ValueTuple`", StringComparison.Ordinal) == true) { var rest = t.GetField("Rest"); if (rest is null) return true; if (!rest.FieldType.FullName?.StartsWith("System.ValueTuple`", StringComparison.Ordinal) == true) return false; t = rest.FieldType; } return true; } Prevention
- Map wide results onto records/classes, not 8+ element tuples.
- Use genuine C# tuples so the compiler nests Rest correctly.
- Avoid hand-built structs that mimic ValueTuple naming.
When it happens
Trigger: Mapping a query result onto a ValueTuple with 8+ elements whose Rest field is not a ValueTuple, or a user-defined struct that mimics ValueTuple layout (named like System.ValueTuple) but has a non-ValueTuple Rest. This is rare with normal C# tuples (the compiler always nests ValueTuples correctly) but can occur with reflection-built types or corrupted/signature-mismatched tuple types.
Common situations: Custom structs deliberately named to look like ValueTuple; very large tuple results (8+ columns mapped to a tuple) combined with a hand-rolled or IL-generated type; version skew where a tuple-shaped type changed its Rest field type.
Related errors
- A single-character was expected
- ValueTuple should not be used for parameters - the language-
- A parameterless default constructor or one matching signatur
- type
- Property setting not found for: {name}
AI-assisted analysis of DapperLib/Dapper@72a54c475f (2026-08-13).
Data as JSON: /api/errors/3b1251ec6060db5b.
Report an issue: GitHub.