gastownhall/beads · error

invalid offset: an offset cannot be combined with a display

Error message

invalid offset: an offset cannot be combined with a display order, because the order is applied to the rows the query bounded and each page would be sorted for itself

What it means

BuildQueryPlan refuses a request that combines Offset > 0 with a SortBy display order. The reason is in the message: the display order applies to the bounded result rows, so each page would be sorted independently and pages would not compose into a global order. The error wraps issueops.ErrValidation.

Source

Thrown at internal/workapi/query.go:76

//
// It reads no configuration and touches no store, so both implementations share
// it without supplying a config source.
func BuildQueryPlan(in issueops.QueryRequest) (QueryPlan, error) {
	expression := strings.TrimSpace(in.Expression)
	if expression == "" {
		return QueryPlan{}, invalidQueryExpression("an expression is required")
	}
	limit := LimitOr(in.Limit, DefaultQueryLimit)
	if limit < 0 {
		return QueryPlan{}, fmt.Errorf("invalid limit %d: a query limit must be zero or greater; 0 means unlimited%.0w",
			limit, issueops.ErrValidation)
	}
	if in.Offset < 0 {
		return QueryPlan{}, fmt.Errorf("invalid offset %d: a query offset must be zero or greater%.0w",
			in.Offset, issueops.ErrValidation)
	}
	if in.Offset > 0 && in.SortBy != "" {
		return QueryPlan{}, fmt.Errorf(
			"invalid offset: an offset cannot be combined with a display order, because the order is applied to the rows the query bounded and each page would be sorted for itself%.0w",
			issueops.ErrValidation)
	}

	node, err := query.Parse(expression)
	if err != nil {
		return QueryPlan{}, invalidQueryExpression(err.Error())
	}
	result, err := query.NewEvaluator(time.Now()).Evaluate(node)
	if err != nil {
		return QueryPlan{}, invalidQueryExpression(err.Error())
	}

	plan := QueryPlan{
		Filter:  result.Filter,
		Limit:   limit,
		Offset:  in.Offset,
		SortBy:  in.SortBy,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Drop the offset and paginate by keyset/cursor when a display order is requested
  2. Clear SortBy when using offset pagination (the query expression itself can express ordering)
  3. Fetch all matching rows with the display order and slice client-side if the set is small

Example fix

// before
req := issueops.QueryRequest{Expression: "status = open", Offset: 20, SortBy: "priority"}
// after
req := issueops.QueryRequest{Expression: "status = open", Offset: 20} // or use cursor pagination with SortBy
Defensive patterns

Strategy: validation

Validate before calling

if req.Offset > 0 && req.SortBy != "" {
	return errors.New("cannot combine offset with display order; use cursor pagination")
}

Try / catch

plan, err := BuildQueryPlan(req)
if err != nil && errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), "display order") {
	// drop SortBy or switch to cursor pagination and retry once
}

Prevention

When it happens

Trigger: Calling BuildQueryPlan with both Offset > 0 and a non-empty in.SortBy — e.g. a client that paginates with offset while also asking for `sort: priority` display ordering.

Common situations: An API client adding both pagination and a sort dropdown; a UI that keeps the previous sort when the user advances to page 2; generated clients that always serialize sort and offset fields.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d865df5208767b61. Report an issue: GitHub.