AvaloniaUI/Avalonia · error · ExpressionParseException

Invalid method call in binding expression: '{node.Method.Dec

Error message

Invalid method call in binding expression: '{node.Method.DeclaringType}.{node.Method.Name}'.

What it means

Thrown by BindingExpressionVisitor.VisitMethodCall when a method invocation in the compiled binding lambda does not match any of the recognized method patterns: (1) get_Item (indexer getter), (2) Get (multi-dimensional array getter), (3) StreamBinding extension methods, or (4) ReflectionExtensions.CreateDelegate. Any other method call is rejected because compiled bindings represent a property path, not arbitrary code execution.

Source

Thrown at src/Avalonia.Base/Data/Core/Parsers/BindingExpressionVisitor.cs:165

            {
                return Add(instance, node, x => x.StreamTask());
            }
            else if (instanceType is not null && ObservableStreamPlugin.MatchesType(instanceType))
            {
                return Add(instance, node, x => x.StreamObservable());
            }
        }
        else if (method == BindingExpressionVisitorMembers.CreateDelegateMethod)
        {
            var methodInfo = GetValue<MethodInfo>(node.Object!);
            var delegateType = GetValue<Type>(node.Arguments[0]);
            return Add(node.Arguments[1], node, x => x.Method(
                methodInfo.MethodHandle,
                delegateType.TypeHandle,
                acceptsNull: false));
        }

        throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType}.{node.Method.Name}'.");
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node == _rootExpression.Parameters[0] && _head is null)
            _head = node;
        return base.VisitParameter(node);
    }
    
    protected override Expression VisitUnary(UnaryExpression node)
    {
        if (node.NodeType == ExpressionType.Not && node.Type == typeof(bool))
        {
            return Add(node.Operand, node, x => x.Not());
        }
        else if (node.NodeType == ExpressionType.Convert)
        {
            // Allow reference type casts (both upcasts and downcasts) but reject value type conversions

View on GitHub (pinned to 11c5427268)

Solutions

  1. Move the method call into a property on the view model: 'public int ItemCount => Items.Count();' and bind to ItemCount.
  2. For formatting, use the binding's StringFormat converter instead of calling ToString() in the lambda.
  3. For stream bindings, use the dedicated '^' stream operator or the StreamBinding() extension method which IS supported.

Example fix

// before: method call in compiled binding
<TextBlock Text="{CompiledBinding Items.Count()}" />

// after: expose as property
<TextBlock Text="{CompiledBinding ItemCount}" />
// view model:
public int ItemCount => Items.Count;
Defensive patterns

Strategy: validation

Validate before calling

// Detect method calls in a binding lambda that are not recognized stream/indexer patterns
static bool HasUnsupportedMethodCall<TIn, TOut>(Expression<Func<TIn, TOut>> expr)
{
    var allowedMethods = new HashSet<string> { "get_Item", "Get" };
    bool found = false;
    expr.Body.Visit(b =>
    {
        if (b is MethodCallExpression mc
            && !allowedMethods.Contains(mc.Method.Name)
            && mc.Method.DeclaringType != typeof(StreamBindingExtensions))
            found = true;
    });
    return found;
}

Try / catch

try
{
    var path = BindingExpressionVisitor<TViewModel>.BuildPath<TProp>(expr);
}
catch (ExpressionParseException ex) when (ex.Message.Contains("Invalid method call"))
{
    logger.LogError($"Compiled binding contains a method call that is not a recognized indexer or stream operation: {ex.Message}");
}

Prevention

When it happens

Trigger: Calling a method in the binding lambda such as x => x.Items.Count() (LINQ method), x => x.Name.ToString(), x => x.Value.ToString(CultureInfo), or any instance/static method call. The C# compiler happily compiles these, but the visitor cannot translate them into a CompiledBindingPath.

Common situations: Using LINQ methods like Count(), First(), Select() in a compiled binding; calling ToString() or other formatting methods; expecting compiled bindings to support method chaining like a fluent API; migrating from code-behind property access to bindings without adjusting the expression.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/035e83139579876a. Report an issue: GitHub.