Repository navigation
NullReferenceException in shaper for tuple query when a query-filtered entity is reached via both a scalar and collection navigation #3933
Copy link
Copy link
Closed
Milestone
Description
We found this while migrating a production application from SQL Server to PostgreSQL -- a query that had worked correctly on SQL Server started throwing this exception against efcore.pg.
Include your code
using Microsoft.EntityFrameworkCore;
using Npgsql;
using Testcontainers.MsSql;
using Testcontainers.PostgreSql;
var sqlServerContainer = new MsSqlBuilder().Build();
var postgresContainer = new PostgreSqlBuilder().Build();
await sqlServerContainer.StartAsync();
await postgresContainer.StartAsync();
await Run("sqlserver", new SqlServerReproContext(sqlServerContainer.GetConnectionString()));
await Run("postgres", new PostgresReproContext(postgresContainer.GetConnectionString()));
await sqlServerContainer.DisposeAsync();
await postgresContainer.DisposeAsync();
static async Task Run(string label, ReproContext context)
{
await using (context)
{
await context.Database.EnsureCreatedAsync();
const short OrgId = 1;
var orderIdentifier = Guid.NewGuid();
var representative = new Representative { Id = 1, OrgId = OrgId, IsActive = true };
var vendor = new Vendor { Id = 1, OrgId = OrgId, IsActive = true, Representatives = [representative] };
var vendorLink = new VendorLink { Id = 1, OrgId = OrgId, Vendor = vendor };
var order = new Order { Id = 1, OrgId = OrgId, OrderIdentifier = orderIdentifier, VendorLinks = [vendorLink] };
context.Orders.Add(order);
await context.SaveChangesAsync();
Console.WriteLine($"--- {label} ---");
try
{
var result = await context.Orders
.Where(x => x.OrderIdentifier == orderIdentifier)
.Select(x => ValueTuple.Create(
x.Id,
x.VendorLinks.Any(vl => vl.Vendor.IsActive && vl.Vendor.Representatives.Any(r => r.IsActive))))
.FirstOrDefaultAsync();
Console.WriteLine($"SUCCESS: ({result.Item1}, {result.Item2})");
}
catch (Exception ex)
{
Console.WriteLine($"FAILED: {ex.GetType().Name}: {ex.Message}");
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine();
}
}
public class Representative
{
public int Id { get; set; }
public short OrgId { get; set; }
public Guid RepresentativeIdentifier { get; set; }
public int VendorId { get; set; }
public bool IsActive { get; set; }
}
public class Vendor
{
public int Id { get; set; }
public short OrgId { get; set; }
public Guid VendorIdentifier { get; set; }
public bool IsActive { get; set; }
public List<Representative> Representatives { get; set; } = [];
}
public class VendorLink
{
public int Id { get; set; }
public short OrgId { get; set; }
public Guid VendorLinkIdentifier { get; set; }
public int OrderId { get; set; }
public int? VendorId { get; set; }
public Vendor? Vendor { get; set; }
}
public class Order
{
public int Id { get; set; }
public short OrgId { get; set; }
public Guid OrderIdentifier { get; set; }
public List<VendorLink> VendorLinks { get; set; } = [];
}
public abstract class ReproContext : DbContext
{
public short OrgId { get; set; } = 1;
public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Representative>().HasKey(e => new { e.OrgId, e.Id });
modelBuilder.Entity<Vendor>(builder =>
{
builder.HasKey(e => new { e.OrgId, e.Id });
builder
.HasMany(v => v.Representatives)
.WithOne()
.HasForeignKey(r => new { r.OrgId, r.VendorId })
.IsRequired();
modelBuilder.Entity<Vendor>().HasQueryFilter(x => x.OrgId == OrgId);
});
modelBuilder.Entity<VendorLink>(builder =>
{
builder.HasKey(e => new { e.OrgId, e.Id });
builder
.HasOne(vl => vl.Vendor)
.WithMany()
.HasForeignKey(vl => new { vl.OrgId, vl.VendorId })
.IsRequired(false);
});
modelBuilder.Entity<Order>(builder =>
{
builder.HasKey(e => new { e.OrgId, e.Id });
builder
.HasMany(o => o.VendorLinks)
.WithOne()
.HasForeignKey(vl => new { vl.OrgId, vl.OrderId })
.IsRequired();
});
}
}
public sealed class SqlServerReproContext(string connectionString) : ReproContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(connectionString);
}
public sealed class PostgresReproContext(string connectionString) : ReproContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var dataSourceBuilder = new NpgsqlDataSourceBuilder(connectionString);
dataSourceBuilder.EnableRecordsAsTuples();
optionsBuilder.UseNpgsql(dataSourceBuilder.Build());
}
}Include the output of your code
Against SQL Server, the query returns (1, True) as expected.
Against Npgsql, it throws:
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.CreateGetValueExpression(ParameterExpression dbDataReader, Int32 index, Boolean nullable, RelationalTypeMapping typeMapping, Type type, IPropertyBase property)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, Expression& relationalCommandResolver, IReadOnlyList`1& readerColumns, LambdaExpression& relatedDataLoaders, Int32& collectionId)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
...
Include provider and version information
EF Core version: 10.0.12
Npgsql.EntityFrameworkCore.PostgreSQL version: 10.0.3
Database server version: PostgreSQL (tested via Testcontainers' default image); also reproduces against Aurora PostgreSQL
Operating system: Windows (also seen in Linux-hosted production)
IDE: n/a (dotnet run)
Further technical details
Removing the query filter on Vendor, or splitting the tuple-projecting query into two separate non-tuple queries, both make it work again.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
roji commentedon Oct 14, 2026
AI Triage
The below is an AI-generated analysis and may contain inaccuracies.
Confirmed bug, specific to the PostgreSQL provider in the tested comparisons. The supplied query reproduces the reported
NullReferenceExceptioninCreateGetValueExpressionwith Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 and EF Core 10.0.12, against PostgreSQL 18.4. The original seeded repro returns(1, True)with SQLite on EF Core 10.0.12. SQL Server was not independently rerun; SQLite was used for the cross-provider check.Confirmed regression between 8.x and 9.x, rather than a new 10.0.12 regression. The reduced repro gives the following results:
NullReferenceExceptionNullReferenceExceptionNullReferenceExceptionNullReferenceExceptionNullReferenceExceptionThe reduced repro also compiles successfully with SQLite on EF Core 10.0.13. No working 9.x or 10.x combination was found among the versions tested.
Reduction and likely cause
The failure happens during query compilation:
ToQueryString()is sufficient, with no database connection, schema creation, or seed data. The outerWhere,FirstOrDefaultAsync, composite keys, andEnableRecordsAsTuples()are unnecessary. However, the otherwise-unusedVendorIdentifierproperty matters: removing it from the original model makes the reduced query compile successfully.A likely provider-side cause is that
PgRowValueExpression.VisitChildrenandUpdateconstruct replacement row-value expressions without preservingTypeMapping. A projection rewrite can therefore leave the tuple projection without a mapping, while EF's shaper expects one. This is a source-based diagnosis, not a validated fix.An equivalent anonymous-object projection compiles successfully on Npgsql 10.0.3 / EF Core 10.0.12, with the query filter retained. Projecting
Idand the boolean into an anonymous object and constructing the tuple after materialization is therefore a workaround candidate that avoids splitting the query.Duplicate search
No convincing duplicate was found in the open or closed EF Core issues searched. dotnet/efcore#39255 has a similar missing-type-mapping/shaper exception, but involves SQL Server JSON primitive collections, not PostgreSQL row values; it is not established as the same bug. #2356 is related historical work on reading row values, rather than a duplicate of this regression.
The issue type has been set to Bug. The tested provider difference and the row-expression mapping code point to keeping this issue in this repository, rather than closing it in favor of an EF Core issue.
minimal repro
Use a .NET 10 console project with implicit usings enabled and these package references:
No running PostgreSQL server is needed: the exception occurs before opening a connection.