{"record":{"id":"db54252b8996d371","repo":"abpframework/abp","slug":"sorting-expression-is-not-supported","errorCode":null,"errorMessage":"Sorting expression is not supported.","messagePattern":"Sorting expression is not supported\\.","errorType":"validation","errorClass":"AbpValidationException","httpStatus":400,"severity":"error","filePath":"framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs","lineNumber":107,"sourceCode":"\n        protected override Expression VisitMethodCall(MethodCallExpression node)\n        {\n            // Allow property-bag / shadow-property access through a constant string\n            // indexer (it[\"Prop\"], Data[\"Prop\"], mainEntity[\"Prop\"], ...). The\n            // constant key cannot smuggle in an arbitrary method invocation, so this\n            // does not widen the injection surface the guard protects against; it\n            // treats the indexer access like ordinary property access.\n            if (IsConstantStringIndexer(node))\n            {\n                if (node.Object != null)\n                {\n                    Visit(node.Object);\n                }\n\n                return node;\n            }\n\n            throw new AbpValidationException(Message);\n        }\n\n        protected override Expression VisitBinary(BinaryExpression node)\n            => throw new AbpValidationException(Message);\n\n        protected override Expression VisitConditional(ConditionalExpression node)\n            => throw new AbpValidationException(Message);\n\n        protected override Expression VisitConstant(ConstantExpression node)\n            => throw new AbpValidationException(Message);\n\n        private static bool IsConstantStringIndexer(MethodCallExpression node)\n        {\n            // Must be an instance call with a single constant string argument.\n            if (node.Object == null\n                || node.Arguments.Count != 1\n                || node.Arguments[0] is not ConstantExpression { Value: string })\n            {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/abpframework/abp/blob/7ed43b1931b9df46a50c0c59148a18645641d0df/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Restrict the sorting string to bare property/field names (e.g. 'Name', 'Age desc') and drop any method-call syntax.","Maintain a server-side whitelist of allowed sortable properties and validate the incoming sorting string against it before applying.","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.","For shadow/property-bag access, use the constant-string indexer form it[\"Prop\"] which the guard explicitly allows."],"exampleFix":"// before: method call in the sort string -> AbpValidationException at :107\nvar list = await repo.GetListAsync(sorting: \"Name.ToUpper()\");\n\n// after: sort by a plain property name (optionally with direction)\nvar list = await repo.GetListAsync(sorting: \"Name\");\n// or, for a computed need, project first then sort by the projected property\nvar dto = query.Select(x => new { x.Id, Upper = x.Name.ToUpper() })\n              .OrderBy(\"Upper\");","handlingStrategy":"validation","validationCode":"static readonly HashSet<string> SortableProperties = new(StringComparer.OrdinalIgnoreCase)\n{\n    \"Name\", \"Age\", \"CreatedOn\", \"Id\"\n};\n\nstatic string? SanitizeSorting(string? sorting)\n{\n    if (sorting.IsNullOrWhiteSpace()) return null;\n    var parts = sorting.Split(',');\n    var accepted = new List<string>();\n    foreach (var raw in parts)\n    {\n        var token = raw.Trim();\n        var descending = token.EndsWith(\" desc\", StringComparison.OrdinalIgnoreCase);\n        var name = token.Substring(0, token.Length - (descending ? 5 : 0)).Trim();\n        if (!SortableProperties.Contains(name))\n        {\n            throw new ArgumentException($\"Sorting property is not allowed: {name}\");\n        }\n        accepted.Add(descending ? $\"{name} desc\" : name);\n    }\n    return string.Join(\", \", accepted);\n}\n\n// usage:\nquery = query.OrderBy(SanitizeSorting(inputSorting));","typeGuard":null,"tryCatchPattern":"try\n{\n    result = await appService.GetListAsync(new PagedAndSortedResultRequestDto { Sorting = sorting });\n}\ncatch (Volo.Abp.Validation.AbpValidationException ex) when (ex.Message.Contains(\"Sorting expression is not supported\"))\n{\n    // surface a 400-friendly error to the client; never echo the raw sorting back unescaped\n    throw new UserFriendlyException(\"Sorting is limited to allowed property names.\");\n}","preventionTips":["Never pass raw client sorting straight to a query; always validate against a property whitelist.","For computed/display columns, project them into DTO properties and whitelist those names.","Reject sorting strings containing '(', ')', or method-call syntax at the API boundary.","Keep the guard installed (it is on by default) — it is a security control against expression injection."],"tags":["sorting","validation","dynamic-linq","expression-trees","security","application-services"],"backgroundTag":null,"analyzedSha":"7ed43b1931b9df46a50c0c59148a18645641d0df","analyzedAt":"2026-08-13T16:26:11.351Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}