abpframework/abp · error · AbpValidationException

Sorting expression is not supported.

Error message

Sorting expression is not supported.

What it means

AbpDynamicSortingGuard installs an ExpressionVisitor (via ExtensibilityPoint.QueryOptimizer) that inspects every OrderBy/ThenBy selector built from a user-supplied sorting string and rejects anything that is not plain property/field access or a constant-string indexer (it["Prop"]). In PropertyOnlySelectorVisitor.VisitMethodCall, after the IsConstantStringIndexer allowance, any remaining method call inside the sort-key lambda throws AbpValidationException ('Sorting expression is not supported.'). This is a security guard: it prevents method invocation, function calls, and arbitrary computation from being smuggled through a dynamic sorting string.

Source

Thrown at framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs:107

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            // Allow property-bag / shadow-property access through a constant string
            // indexer (it["Prop"], Data["Prop"], mainEntity["Prop"], ...). The
            // constant key cannot smuggle in an arbitrary method invocation, so this
            // does not widen the injection surface the guard protects against; it
            // treats the indexer access like ordinary property access.
            if (IsConstantStringIndexer(node))
            {
                if (node.Object != null)
                {
                    Visit(node.Object);
                }

                return node;
            }

            throw new AbpValidationException(Message);
        }

        protected override Expression VisitBinary(BinaryExpression node)
            => throw new AbpValidationException(Message);

        protected override Expression VisitConditional(ConditionalExpression node)
            => throw new AbpValidationException(Message);

        protected override Expression VisitConstant(ConstantExpression node)
            => throw new AbpValidationException(Message);

        private static bool IsConstantStringIndexer(MethodCallExpression node)
        {
            // Must be an instance call with a single constant string argument.
            if (node.Object == null
                || node.Arguments.Count != 1
                || node.Arguments[0] is not ConstantExpression { Value: string })
            {

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Restrict the sorting string to bare property/field names (e.g. 'Name', 'Age desc') and drop any method-call syntax.
  2. Maintain a server-side whitelist of allowed sortable properties and validate the incoming sorting string against it before applying.
  3. Map computed/display needs to a real projected property (Select into a DTO) and sort by that property name instead of invoking a method in the sort key.
  4. For shadow/property-bag access, use the constant-string indexer form it["Prop"] which the guard explicitly allows.

Example fix

// before: method call in the sort string -> AbpValidationException at :107
var list = await repo.GetListAsync(sorting: "Name.ToUpper()");

// after: sort by a plain property name (optionally with direction)
var list = await repo.GetListAsync(sorting: "Name");
// or, for a computed need, project first then sort by the projected property
var dto = query.Select(x => new { x.Id, Upper = x.Name.ToUpper() })
              .OrderBy("Upper");
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<string> SortableProperties = new(StringComparer.OrdinalIgnoreCase)
{
    "Name", "Age", "CreatedOn", "Id"
};

static string? SanitizeSorting(string? sorting)
{
    if (sorting.IsNullOrWhiteSpace()) return null;
    var parts = sorting.Split(',');
    var accepted = new List<string>();
    foreach (var raw in parts)
    {
        var token = raw.Trim();
        var descending = token.EndsWith(" desc", StringComparison.OrdinalIgnoreCase);
        var name = token.Substring(0, token.Length - (descending ? 5 : 0)).Trim();
        if (!SortableProperties.Contains(name))
        {
            throw new ArgumentException($"Sorting property is not allowed: {name}");
        }
        accepted.Add(descending ? $"{name} desc" : name);
    }
    return string.Join(", ", accepted);
}

// usage:
query = query.OrderBy(SanitizeSorting(inputSorting));

Try / catch

try
{
    result = await appService.GetListAsync(new PagedAndSortedResultRequestDto { Sorting = sorting });
}
catch (Volo.Abp.Validation.AbpValidationException ex) when (ex.Message.Contains("Sorting expression is not supported"))
{
    // surface a 400-friendly error to the client; never echo the raw sorting back unescaped
    throw new UserFriendlyException("Sorting is limited to allowed property names.");
}

Prevention

When it happens

Trigger: Applying a dynamic sort whose parsed selector contains a method call that is not a string-keyed indexer with a constant argument. With System.Linq.Dynamic.Core this happens for inputs like "Name.ToUpper()", "x.Substring(0,3)", "Guid()", or any function-style token in the sorting string passed to a paged/result query that the guard then visits. The throw originates at AbpDynamicSortingGuard.cs:107.

Common situations: Passing raw user input as the sorting parameter to an application service query; front-end sending computed display expressions instead of raw property names; sorting strings that try to call formatting or conversion methods; a DTO with a computed property that the client references through a method rather than a mapped property.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/db54252b8996d371. Report an issue: GitHub.