dotnet/efcore · error · NotSupportedException

Encountered a constant of unsupported type

Error message

Encountered a constant of unsupported type '{value.GetType().Name}'. Only primitive constant nodes are supported.

What it means

Thrown by LinqToCSharpSyntaxTranslator.GenerateUnknownValue, which is the fallback arm of GenerateValue when a constant value is not one of the supported kinds (primitives, string, enum, Type, Guid, well-known CultureInfo/Encoding/Comparer singletons, ValueTuple) and is not an IRelationalQuotableExpression that can be quoted. The translator cannot emit a C# literal for arbitrary reference types, so it rejects them.

Solutions

  1. Pass a constantReplacements dictionary to TranslateExpression/TranslateStatement that maps the captured value to a parameter/variable name the generated code can reference.
  2. Refactor the query to capture only primitives (ids, strings) and look up the complex object inside the lambda via a parameter instead of a closure.
  3. If the value type implements IRelationalQuotableExpression, make sure its Quote() returns a translatable expression; otherwise it will still fall through to this throw.
  4. Replace the captured object with a primitive key and resolve the object from a service inside the lambda.

Example fix

// before — complex object captured as a constant
var settings = new QuerySettings { Threshold = 10 };
var pred = Expression.Lambda(
    Expression.GreaterThan(
        Expression.Property(param, "Score"),
        Expression.Constant(settings.Threshold)));
// (GenerateValue hits the fallback if 'settings' itself is the constant)

// after — pass a constant replacement mapping the object to a variable name
var replacements = new Dictionary<object, string>
{
    { settings, "settings" }
};
translator.TranslateExpression(
    pred.Body, replacements, namespaces, unsafeAccessors);
Defensive patterns

Strategy: validation

Validate before calling

// Pre-scan constants and require either a replacement mapping or a supported kind.
using System.Linq.Expressions;
using System.Collections;
using System.Globalization;
using System.Text;

bool AllConstantsSupported(Expression e, IReadOnlyDictionary<object, string>? replacements)
{
    bool ok = true;
    new ConstantChecker(c =>
    {
        if (c.Value is null) return;
        if (replacements is not null && replacements.ContainsKey(c.Value)) return;
        switch (c.Value)
        {
            case int or long or uint or ulong or short or sbyte or ushort or byte
                 or double or float or decimal or char or string or bool:
            case Enum:
            case Type:
            case Guid:
            case ITuple:
            case ReferenceEqualityComparer:
            case CultureInfo:
            case Encoding:
                return;
            default:
                ok = false;
                break;
        }
    }).Visit(e);
    return ok;
}

class ConstantChecker(Action<ConstantExpression> onConstant) : ExpressionVisitor
{
    protected override Expression VisitConstant(ConstantExpression c) { onConstant(c); return base.VisitConstant(c); }
}

Prevention

When it happens

Trigger: An expression tree being rendered to C# contains a ConstantExpression whose Value is a complex/custom object (a DTO, a closure class instance, a dictionary, etc.) and no constant replacement was supplied to substitute a variable name for it.

Common situations: Capturing a non-primitive variable in a query (e.g. a settings object, a list, a custom comparer) when generating compiled-query C#; provider extensions that wrap complex state as constants; running LinqToCSharpSyntaxTranslator without constantReplacements for captured closures.

Related errors


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

Appendix: source

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

        }
    }

    /// <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 virtual ExpressionSyntax GenerateUnknownValue(object value)
    {
        var type = value.GetType();
        return type.IsValueType
            && value.Equals(type.GetDefaultValue())
                ? DefaultExpression(Generate(type))
                : value is IRelationalQuotableExpression relationalQuotableExpression
                && Translate(relationalQuotableExpression.Quote()) is ExpressionSyntax expressionSyntax
                    ? expressionSyntax
                    : throw new NotSupportedException(
                        $"Encountered a constant of unsupported type '{value.GetType().Name}'. Only primitive constant nodes are supported."
                        + Environment.NewLine
                        + value);
    }

    /// <inheritdoc />
    protected override Expression VisitDebugInfo(DebugInfoExpression node)
        => throw new NotSupportedException("DebugInfo nodes are not supporting when translating expression trees to C#");

    /// <inheritdoc />
    protected override Expression VisitDefault(DefaultExpression node)
    {
        Result = DefaultExpression(Generate(node.Type));

        return node;
    }

    /// <inheritdoc />

View on GitHub (pinned to 3a2006ef56)