laravel/framework · error · RuntimeException
You must specify an orderBy clause when using this function.
Error message
You must specify an orderBy clause when using this function.
What it means
Thrown by enforceOrderBy when both $this->orders and $this->unionOrders are empty. The guard is invoked by ensureOrderForCursorPagination, so cursorPaginate/cursorPaginateUsingCursor require a deterministic row order to compute stable cursors. Without an orderBy the cursor has nothing to anchor against and pagination would return arbitrary or duplicated rows across pages.
Source
Thrown at src/Illuminate/Database/Query/Builder.php:3811
yield from $this->connection->cursor(
$this->toSql(), $this->getBindings(), ! $this->useWritePdo, $this->fetchUsing
);
}))->map(function ($item) {
return $this->applyAfterQueryCallbacks(new Collection([$item]))->first();
})->reject(fn ($item) => is_null($item));
}
/**
* Throw an exception if the query doesn't have an orderBy clause.
*
* @return void
*
* @throws \RuntimeException
*/
protected function enforceOrderBy()
{
if (empty($this->orders) && empty($this->unionOrders)) {
throw new RuntimeException('You must specify an orderBy clause when using this function.');
}
}
/**
* Get a collection instance containing the values of a given column.
*
* @param \Illuminate\Contracts\Database\Query\Expression|string $column
* @param string|null $key
* @return \Illuminate\Support\Collection<array-key, mixed>
*/
public function pluck($column, $key = null)
{
$original = $this->columns;
// First, we will need to select the results of the query accounting for the
// given columns / key. Once we have the results, we will be able to take
// the results and get the exact data that was requested for the query.
$this->columns ??= is_null($key) || $key === $columnView on GitHub (pinned to bd6b5437e6)
Solutions
- Add an orderBy before cursorPaginate: `Model::orderBy('id')->cursorPaginate()`.
- Prefer ordering by a unique, monotonic column (id, created_at with id tiebreaker) for stable cursors.
- For unions, order the outer query: `DB::table(...)->union(...)->orderBy('id')->cursorPaginate()`.
- If order is genuinely irrelevant, use simple `paginate()`/`get()` instead of cursor pagination.
Example fix
// before
Model::cursorPaginate(20);
// => You must specify an orderBy clause when using this function.
// after
Model::orderBy('id')->cursorPaginate(20); Defensive patterns
Strategy: validation
Validate before calling
if (empty($query->orders) && empty($query->unionOrders)) {
$query->orderBy($model->getKeyName());
}
$model::cursorPaginate(); Type guard
function hasOrderBy(\Illuminate\Database\Query\Builder $q): bool
{
return ! empty($q->orders) || ! empty($q->unionOrders);
} Try / catch
try {
$results = $query->cursorPaginate();
} catch (\RuntimeException $e) {
if (str_contains($e->getMessage(), 'orderBy clause')) {
$query->orderBy($query->from.'.id');
$results = $query->cursorPaginate();
} else { throw $e; }
} Prevention
- Always chain ->orderBy('id') (or another unique monotonic column) before cursorPaginate.
- For unions, order the outer builder.
- If order is irrelevant, switch to simple paginate() instead.
When it happens
Trigger: Calling `Model::cursorPaginate()` without any prior `->orderBy(...)`. Using cursor pagination on a view or query that has no natural ordering. Chaining `->orderByRaw('')` (which adds nothing). Switching from paginate() to cursorPaginate() on an unordered query.
Common situations: Adopting cursor pagination for performance on an existing endpoint that never specified order; paginating a UNION query where orders are set on a subquery not the outer builder; default scope stripping order.
Related errors
- Order direction must be a SortDirection, "asc" or "desc".
- The chunkById operation was aborted because the [{$alias}] c
- No record found for the given query.
- $count records were found.
- Property [{$key}] does not exist on the Eloquent builder ins
AI-assisted analysis of laravel/framework@bd6b5437e6 (2026-08-06).
Data as JSON: /data/errors/e6570e8c8b1e3935.json.
Report an issue: GitHub.