The LINQ expression '...' could not be translated means EF Core met something in your query that it cannot turn into SQL, most often a call to your own C# method inside Where. Write the condition as an expression EF Core can read, either inline or as an Expression<Func<T, bool>>, and the database does the filtering again.

The message also offers AsEnumerable or ToList to run the filter in memory. That works, but it downloads the whole table first, so treat it as a deliberate choice for small tables, not as the fix.

The error

System.InvalidOperationException: The LINQ expression 'DbSet<Customer>()
    .Where(c => CustomerRules.IsVip(c))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)

Why it happens

The rule lived in a helper so it could be reused:

public static bool IsVip(Customer c) => c.TotalSpent >= VipThreshold;
app.MapGet("/customers/vip-helper", async (LoyaltyDb db) =>
    await db.Customers.Where(c => CustomerRules.IsVip(c)).ToListAsync());

A lambda passed to Where on a DbSet is not compiled code; it is an expression tree, a description of the code that EF Core reads and rewrites as SQL. EF Core can read c.TotalSpent >= 1000 in that tree. It cannot read inside IsVip: the tree only records "call this method with c", and the method's body is compiled IL with no SQL equivalent. The query fails while it is being compiled, before any SQL is sent, which is why the stack trace ends in the query translator and the log shows no database command for that request.

The fix

Keep the rule in one place, but store it as an expression rather than a method:

public static class CustomerRules
{
    public const decimal VipThreshold = 1000m;

    public static bool IsVip(Customer c) => c.TotalSpent >= VipThreshold;

    public static readonly Expression<Func<Customer, bool>> IsVipExpr =
        c => c.TotalSpent >= VipThreshold;
}
app.MapGet("/customers/vip", async (LoyaltyDb db) =>
    await db.Customers.Where(CustomerRules.IsVipExpr).ToListAsync());

Writing the condition inline, Where(c => c.TotalSpent >= CustomerRules.VipThreshold), works too. Both produced the same SQL, with the filter in the database:

SELECT c."Id", c."Name", c."TotalSpent"
FROM "Customers" AS c
WHERE c."TotalSpent" >= 1000.0

The deliberate client-side version is this:

app.MapGet("/customers/vip-client", (LoyaltyDb db) =>
    db.Customers.AsEnumerable().Where(CustomerRules.IsVip).ToList());

It returned the same two customers, but its SQL had no WHERE clause: every row came over the wire and was filtered in memory. With four customers nobody notices; with four hundred thousand, the endpoint gets slow and memory-hungry. Use it only when the table is small or the logic truly cannot be expressed in SQL, and filter as much as you can before the AsEnumerable call.

How it was reproduced

A minimal API from dotnet new web with Microsoft.EntityFrameworkCore 10.0.12 and Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, a Customers table with four fictional rows, and a GET endpoint whose Where called a static C# helper. It returned 500 with the error above; the expression and inline versions returned the two customers above the threshold, with EF Core's command log showing the SQL. .NET SDK 10.0.401 on Windows, PostgreSQL 18.4 in the postgres:18 Docker image.

Frequently asked

Why can't EF Core translate my C# method in a Where clause?
EF Core reads the lambda as an expression tree and turns it into SQL. A call to your own method appears in that tree only as a method call; its body is compiled code that EF Core cannot read, so there is nothing to translate.
How do I reuse a filter in EF Core LINQ queries?
Store it as an Expression<Func<T, bool>> field or property and pass it to Where. EF Core can read and translate an expression, so the condition runs in the database, and you still keep the rule in one place.
Is it bad to use AsEnumerable to fix could not be translated?
It works, but everything after AsEnumerable runs in memory, so the query without that part is sent to the database. For an unfiltered table that means every row is downloaded. Use it deliberately, after filtering as much as possible in SQL.

More decoded errors in the Fixes category. The difference between an in-memory IEnumerable and a translated IQueryable is explained in the collections and LINQ interview questions.