dotnet/efcore · error · NotImplementedException

Power over non-double operands

Error message

Power over non-double operands

What it means

Thrown by LinqToCSharpSyntaxTranslator.VisitBinary when an ExpressionType.Power binary is encountered whose operands are not both System.Double. Power over double is lowered to a call to Math.Pow(double,double); any other operand type (int, decimal, float, etc.) is unhandled and produces this NotImplementedException.

Solutions

  1. Cast both operands of Expression.Power to double before constructing the Power node, and cast the result back if needed.
  2. Replace Expression.Power with an explicit Expression.Call(typeof(Math), nameof(Math.Pow), null, leftAsDouble, rightAsDouble).
  3. If the integer/decimal power semantics are required, compute the value outside the expression tree and inline the result as a constant.

Example fix

// before
var power = Expression.Power(
    Expression.Constant(2),
    Expression.Constant(10)); // both int -> throws

// after
var power = Expression.Power(
    Expression.Convert(Expression.Constant(2), typeof(double)),
    Expression.Convert(Expression.Constant(10), typeof(double)));
Defensive patterns

Strategy: validation

Validate before calling

// Reject non-double Power operands before translating to C#.
using System.Linq.Expressions;

bool IsSupportedPower(BinaryExpression b)
    => b.NodeType == ExpressionType.Power
       && b.Left.Type == typeof(double)
       && b.Right.Type == typeof(double);

// In a visitor, ensure all Power nodes are double-typed before handing the tree off.

Type guard

static bool IsDoublePower(BinaryExpression b)
    => b.NodeType == ExpressionType.Power
       && b.Left.Type == typeof(double)
       && b.Right.Type == typeof(double);

Prevention

When it happens

Trigger: An expression tree handed to LinqToCSharpSyntaxTranslator contains an Expression.Power node whose Left.Type or Right.Type is not double — e.g. an int power built via Expression.Power(Expression.Constant(2), Expression.Constant(3)), or a decimal power.

Common situations: A query-translation extension or a manually-built expression tree uses Expression.Power over integer/decimal operands and then feeds the result to the C# syntax translator (compiled-query source generation); EF provider extensions that materialize Power expressions for non-double numerics.

Related errors


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

Appendix: source

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

            case ExpressionType.OrAssign:
                return VisitAssignment(binary, SyntaxKind.OrAssignmentExpression);
            case ExpressionType.LeftShiftAssign:
                return VisitAssignment(binary, SyntaxKind.LeftShiftAssignmentExpression);
            case ExpressionType.RightShiftAssign:
                return VisitAssignment(binary, SyntaxKind.RightShiftAssignmentExpression);
            case ExpressionType.ExclusiveOrAssign:
                return VisitAssignment(binary, SyntaxKind.ExclusiveOrAssignmentExpression);

            case ExpressionType.Power when binary.Left.Type == typeof(double) && binary.Right.Type == typeof(double):
                return Visit(
                    Expression.Call(
                        _mathPowMethod ??= typeof(Math).GetMethod(
                            nameof(Math.Pow), BindingFlags.Static | BindingFlags.Public, [typeof(double), typeof(double)])!,
                        binary.Left,
                        binary.Right));

            case ExpressionType.Power:
                throw new NotImplementedException("Power over non-double operands");

            case ExpressionType.PowerAssign:
                return Visit(
                    Expression.Assign(
                        binary.Left,
                        Expression.Power(
                            binary.Left,
                            binary.Right)));
        }

        var liftedStatementOrigPosition = _liftedState.Statements.Count;
        var left = Translate<ExpressionSyntax>(binary.Left);
        var liftedStatementLeftPosition = _liftedState.Statements.Count;
        var right = Translate<ExpressionSyntax>(binary.Right);

        // If both sides were lifted, we don't need to do anything special. Same if the left side was lifted.
        // But if the right side was lifted and the left wasn't, then in order to preserve evaluation order we need to lift the left side
        // out as well, otherwise the right side gets evaluated before the left.

View on GitHub (pinned to 3a2006ef56)