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

  1. Sort only by real, allowlisted column fields
  2. Fix typos/case in the sort field name
  3. Add the intended sort field to the field resolver's allowed fields if sorting on it is legitimate
  4. 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

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


AI-assisted analysis of pocketbase/pocketbase@5d217ddb50 (2026-08-15). Data as JSON: /api/errors/b600da4c027c44aa. Report an issue: GitHub.