pocketbase/pocketbase · error
invalid sort field %q
Error message
invalid sort field %q
What it means
Produced when building an ORDER BY expression: the sort field either fails to resolve via the field resolver, resolves to something with bound parameters, resolves to an empty identifier, or resolves to the literal `NULL`. Only plain column identifiers are sortable, so any non-column result (e.g. a macro expansion or a parameterized value) is rejected as an invalid sort field.
Source
Thrown at tools/search/sort.go:41
}
// BuildExpr resolves the sort field into a valid db sort expression.
func (s *SortField) BuildExpr(fieldResolver FieldResolver) (string, error) {
// special case for random sort
if s.Name == randomSortKey {
return "RANDOM()", nil
}
// special case for the builtin SQLite rowid column
if s.Name == rowidSortKey {
return fmt.Sprintf("[[_rowid_]] %s", s.Direction), nil
}
result, err := fieldResolver.Resolve(s.Name)
// invalidate empty fields and non-column identifiers
if err != nil || len(result.Params) > 0 || result.Identifier == "" || strings.ToLower(result.Identifier) == "null" {
return "", fmt.Errorf("invalid sort field %q", s.Name)
}
return fmt.Sprintf("%s %s", result.Identifier, s.Direction), nil
}
// ParseSortFromString parses the provided string expression
// into a slice of SortFields.
//
// Example:
//
// fields := search.ParseSortFromString("-name,+created")
func ParseSortFromString(str string) (fields []SortField) {
data := strings.Split(str, ",")
for _, field := range data {
// trim whitespaces
field = strings.TrimSpace(field)
if strings.HasPrefix(field, "-") {View on GitHub (pinned to 5d217ddb50)
Solutions
- Sort only by real, allowlisted column fields
- Fix typos/case in the sort field name
- Add the intended sort field to the field resolver's allowed fields if sorting on it is legitimate
- Remove macros or expressions from the sort parameter — they are only valid in filters
Example fix
// before sort := "@now" // or "nonExistentField" // after sort := "created"
Defensive patterns
Strategy: validation
Validate before calling
// validate sort fields before building the query
for _, sf := range search.ParseSortFromString(sortParam) {
if _, err := resolver.Resolve(sf.Name); err != nil {
return fmt.Errorf("invalid sort field %q", sf.Name)
}
} Type guard
func isSortableField(name string, resolver search.FieldResolver) bool {
r, err := resolver.Resolve(name)
return err == nil && r.Identifier != "" && len(r.Params) == 0 && strings.ToLower(r.Identifier) != "null"
} Try / catch
expr, err := search.ParseSort(sortParam).BuildExpr(resolver)
if err != nil {
if strings.Contains(err.Error(), "invalid sort field") {
// drop bad sort fields and retry with a safe default like "id"
}
} Prevention
- Expose only allowlisted fields as sortable in API contracts
- Strip macros and expressions from sort parameters at the API boundary
- Return 400 with the offending field name instead of a 500 when sort resolution fails
- Keep client sort lists synchronized with schema renames
When it happens
Trigger: Sorting by a field not in the resolver's allowlist (e.g. `sort=secretField`); sorting by `@now` or another macro (resolves to a bound parameter); sorting by an identifier that resolves to empty/`NULL`; using `sort=` with a mistyped column name.
Common situations: API requests with `sort=` parameters naming non-allowlisted or computed fields; renaming schema fields while client code still sorts by the old name; trying to sort by JSON path or relation fields not permitted by the resolver.
Related errors
- failed to resolve field %q
- empty query
- validation_invalid_view_query
- reached the max recursion level of view collection file fiel
- no query file field found
AI-assisted analysis of pocketbase/pocketbase@5d217ddb50 (2026-08-15).
Data as JSON: /api/errors/b600da4c027c44aa.
Report an issue: GitHub.