dotnet/efcore · error · InvalidOperationException

'VisitChildren' must be overridden in the class deriving fro

Error message

'VisitChildren' must be overridden in the class deriving from 'SqlExpression'.

What it means

The abstract SqlExpression.VisitChildren intentionally throws InvalidOperationException to force every concrete subclass to override VisitChildren (so expression-tree rewriting visits child SqlExpressions correctly). Hitting this means a SqlExpression subclass was visited without overriding the method.

Source

Thrown at src/EFCore.Cosmos/Query/Internal/Expressions/SqlExpression.cs:41

    /// </summary>
    public override Type Type { get; } = type;

    /// <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>
    public virtual CoreTypeMapping? TypeMapping { get; } = typeMapping;

    /// <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 VisitChildren(ExpressionVisitor visitor)
        => throw new InvalidOperationException(CosmosStrings.VisitChildrenMustBeOverridden);

    /// <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>
    public sealed override ExpressionType NodeType
        => ExpressionType.Extension;

    /// <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 abstract void Print(ExpressionPrinter expressionPrinter);

View on GitHub (pinned to dbf9771522)

Solutions

  1. Override VisitChildren in every custom SqlExpression subclass, returning a new instance when children change (and `this` when they don't).
  2. Ensure internal expression types you depend on are from a consistent EF Core build (no mixed versions).
  3. If you are an end user not subclassing SqlExpression, report it as a provider bug.

Example fix

// before
class MyExpr : SqlExpression { /* no VisitChildren override -> throws on visit */ }

// after
protected override Expression VisitChildren(ExpressionVisitor visitor)
{
    var child = (SqlExpression)visitor.Visit(Child);
    return child == Child ? this : new MyExpr(child);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// For custom SqlExpression subclasses, assert VisitChildren is overridden at startup.
foreach (var t in Assembly.GetExecutingAssembly().GetTypes()
            .Where(t => typeof(SqlExpression).IsAssignableFrom(t) && !t.IsAbstract))
    if (t.GetMethod(nameof(Expression.VisitChildren),
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
        Type.DefaultBinder, new[] { typeof(ExpressionVisitor) }, null)?.DeclaringType != t)
        throw new InvalidOperationException($"{t} must override VisitChildren.");

Type guard

static bool OverridesVisitChildren(Type t)
    => t.GetMethod(nameof(Expression.VisitChildren),
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public,
        Type.DefaultBinder, new[] { typeof(ExpressionVisitor) }, null)?.DeclaringType == t;

Try / catch

try { visitor.Visit(mySqlExpression); }
catch (InvalidOperationException ex) when (ex.Message.Contains("VisitChildren"))
{ /* add VisitChildren override to the custom SqlExpression subclass */ }

Prevention

When it happens

Trigger: A custom SqlExpression subclass that did not override VisitChildren is passed through an ExpressionVisitor; or an internal expression type lost its override after a refactor.

Common situations: Writing a custom Cosmos query extension that introduces a new SqlExpression-derived type; provider regression after upgrades.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/345fb1eb79eddb64. Report an issue: GitHub.