dotnet/efcore · error · NotSupportedException

Encountered non-quotable expression of type

Error message

Encountered non-quotable expression of type {node.GetType()} when translating expression tree to C#

What it means

Thrown by VisitExtension when an extension Expression node is encountered that is not an EntityQueryRootExpression. The translator special-cases EntityQueryRootExpression (rendering it as context.Set<TEntity>()), but any other extension node type is considered non-quotable into C# and throws NotSupportedException with the node's runtime type.

Solutions

  1. Remove the unsupported extension node from the query or replace it with a standard expression EF can translate.
  2. If the node is an EF-internal query root, ensure it is an EntityQueryRootExpression so it renders as context.Set<T>().
  3. Report the node type to EF if it appears in a normal query (possible translator gap).
Defensive patterns

Strategy: validation

Validate before calling

// Reject non-EntityQueryRootExpression extension nodes
static bool HasUnknownExtension(Expression e)
{
    bool bad = false;
    new ExtensionChecker { OnBad = () => bad = true }.Visit(e);
    return bad;
}
class ExtensionChecker : ExpressionVisitor
{
    public Action OnBad;
    protected override Expression VisitExtension(Expression node)
    {
        if (node is not EntityQueryRootExpression) OnBad();
        return base.VisitExtension(node);
    }
}

Type guard

static bool IsTranslatableExtension(Expression node) => node is EntityQueryRootExpression;

Try / catch

try { var csharp = translator.Translate(expr); }
catch (NotSupportedException ex) when (ex.Message.Contains("non-quotable expression"))
{ throw new InvalidOperationException($"Query contains an extension node type EF cannot translate to C#: {ex.Message}.", ex); }

Prevention

When it happens

Trigger: An expression tree passed to the translator contains a custom or EF-internal Expression extension type that is not EntityQueryRootExpression (e.g. a proprietary query root, a specialized EF extension node, or a user-defined Expression subclass).

Common situations: A precompiled query references an extension expression the translator has not been taught to handle; mixing in provider-specific extension nodes; upgrading EF where a new internal extension node appears before translator support is added.

Related errors


AI-assisted analysis of dotnet/efcore@3a2006ef56 (2026-08-11). Data as JSON: /api/errors/61187ff70779a3b6. Report an issue: GitHub.

Appendix: source

Thrown at src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs:2691

    /// <summary>
    ///     This is an internal API that supports the Entity Framework Core infrastructure and not subject to
    ///     the same compatibility standards as public APIs. It may be changed or removed without notice in
    ///     any release. You should only use it directly in your code with extreme caution and knowing that
    ///     doing so can result in application failures when updating to a new Entity Framework Core release.
    /// </summary>
    protected override Expression VisitExtension(Expression node)
    {
        // TODO: Remove any EF-specific code from this visitor (extend if needed)
        // TODO: Hack mode. Visit the expression beforehand to replace EntityQueryRootExpression with context.Set<>(), or receive it in this visitor as a replacement or something.
        if (node is EntityQueryRootExpression entityQueryRoot)
        {
            // TODO: STET
            Result = ParseExpression($"context.Set<{entityQueryRoot.EntityType.ClrType.Name}>()");
            return node;
        }

        throw new NotSupportedException(
            $"Encountered non-quotable expression of type {node.GetType()} when translating expression tree to C#");
    }

    private ArgumentSyntax[] TranslateMethodArguments(ParameterInfo[] parameters, IReadOnlyList<Expression> arguments)
    {
        var translatedExpressions = TranslateList(arguments);
        var translatedArguments = new ArgumentSyntax[arguments.Count];

        for (var i = 0; i < translatedExpressions.Length; i++)
        {
            var parameter = parameters[i];
            var argument = Argument(translatedExpressions[i]);

            if (parameter.IsOut)
            {
                argument = argument.WithRefKindKeyword(Token(SyntaxKind.OutKeyword));
            }
            else if (parameter.IsIn)

View on GitHub (pinned to 3a2006ef56)