AvaloniaUI/Avalonia · error · ExpressionParseException

Unable to parse '{expression}': expected an instance of '{_h

Error message

Unable to parse '{expression}': expected an instance of '{_head}' but got '{visited}'.

What it means

BindingExpressionVisitor maintains a single `_head` representing the current end of the path. The private Add method re-visits the instance of each new node and requires it to equal `_head`; if the visited instance is not the current head (broken chain, method on a different object, static that detaches from the parameter), it throws ExpressionParseException. This is the generic 'the expression is not a continuous path rooted at the lambda parameter' error.

Source

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

    }

    protected override Expression VisitTry(TryExpression node)
    {
        throw new ExpressionParseException(0, $"Invalid expression type in binding expression: {node.NodeType}.");
    }

    protected override Expression VisitTypeBinary(TypeBinaryExpression node)
    {
        throw new ExpressionParseException(0, $"Invalid expression type in binding expression: {node.NodeType}.");
    }

    private Expression Add(Expression? instance, Expression expression, Action<CompiledBindingPathBuilder> build)
    {
        var visited = Visit(instance);

        if (visited != _head)
        {
            throw new ExpressionParseException(
                0,
                $"Unable to parse '{expression}': expected an instance of '{_head}' but got '{visited}'.");
        }

        build(_builder);
        return _head = expression;
    }

    private Expression AddPropertyNode(MemberExpression node)
    {
        // Check if it's an AvaloniaProperty accessed via CLR wrapper
        if (typeof(AvaloniaObject).IsAssignableFrom(node.Expression?.Type) &&
            AvaloniaPropertyRegistry.Instance.FindRegistered(node.Expression.Type, node.Member.Name) is { } avaloniaProperty)
        {
            return Add(
                node.Expression,
                node,
                x => x.Property(avaloniaProperty, CreateAvaloniaPropertyAccessor));

View on GitHub (pinned to 11c5427268)

Solutions

  1. Make the binding a single continuous chain rooted at the lambda parameter (x => x.A.B.C).
  2. Move helper calls into a view-model property or an IValueConverter.
  3. Apply StreamBinding to the current path tail: x => x.Loader.StreamBinding().

Example fix

// before
CompiledBinding.For(x => Helper.Format(x.Name));
// after - expose formatted name on the VM
CompiledBinding.For(x => x.FormattedName);
Defensive patterns

Strategy: validation

Validate before calling

// Verify the lambda body chains continuously from its single parameter
// (every node's instance resolves to the previous node's result).
// Quick check: the body must reference exactly the lambda's parameter as root.
var param = expr.Parameters[0];
bool rooted = new RootChecker(param).IsRooted(expr.Body);

Try / catch

try { var path = BindingExpressionVisitor<TIn>.BuildPath<TOut>(expr); }
catch (ExpressionParseException ex) when (ex.Message.Contains("expected an instance of"))
{
    // restructure the lambda into a single continuous chain
}

Prevention

When it happens

Trigger: CompiledBinding.For(x => Helper.Format(x.Name)) where the method operates on a different object than the path head; mixing unrelated sub-expressions; a StreamBinding or delegate call applied to something other than the current path result.

Common situations: Calling static/instance helpers on objects other than the current path result; building expression trees where a node's instance does not chain from the previous node; incorrect StreamBinding placement.

Understand the failure class

Related errors


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