phacility/phabricator · error · Exception

Order vector "%s" is invalid: the last column in an order mu

Error message

Order vector "%s" is invalid: the last column in an order must be a column with unique values, but "%s" is not unique.

What it means

Thrown by setOrderVector() when the LAST column of the order vector is not marked 'unique' => true in getOrderableColumns(). Cursor paging requires a strong (total) ordering — rows must have a fully deterministic sequence — which is only guaranteed if the final sort column has unique values (typically the object 'id'). Without it, paging could skip or repeat rows, so the API refuses the vector.

Source

Thrown at src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php:1072

      if (empty($orderable[$key])) {
        $valid = implode(', ', array_keys($orderable));
        throw new Exception(
          pht(
            'This query ("%s") does not support sorting by order key "%s". '.
            'Supported orders are: %s.',
            get_class($this),
            $key,
            $valid));
      }

      $unique[$key] = idx($orderable[$key], 'unique', false);
    }

    // Make sure that the last column is unique so that this is a strong
    // ordering which can be used for paging.
    $last = last($unique);
    if ($last !== true) {
      throw new Exception(
        pht(
          'Order vector "%s" is invalid: the last column in an order must '.
          'be a column with unique values, but "%s" is not unique.',
          $vector->getAsString(),
          last_key($unique)));
    }

    // Make sure that other columns are not unique; an ordering like "id, name"
    // does not make sense because only "id" can ever have an effect.
    array_pop($unique);
    foreach ($unique as $key => $is_unique) {
      if ($is_unique) {
        throw new Exception(
          pht(
            'Order vector "%s" is invalid: only the last column in an order '.
            'may be unique, but "%s" is a unique column and not the last '.
            'column in the order.',
            $vector->getAsString(),

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Append a unique column (normally 'id') as the final component: setOrderVector(array('title', 'id'))
  2. If your custom column is genuinely unique (e.g. a unique hash), declare 'unique' => true for it in getOrderableColumns()

Example fix

// before
$query->setOrderVector(array('title'));

// after
$query->setOrderVector(array('title', 'id'));
Defensive patterns

Strategy: validation

Validate before calling

// Guarantee a unique terminator before setting the vector.
$orderable = $query->getOrderableColumns();
$last = last($vector);
if (!idx($orderable[$last], 'unique', false)) {
  $vector[] = 'id'; // unique on every cursor-paged query
}
$query->setOrderVector($vector);

Type guard

function vectorEndsUnique(PhabricatorCursorPagedPolicyAwareQuery $query, array $vector) {
  $orderable = $query->getOrderableColumns();
  return idx($orderable[last($vector)], 'unique', false) === true;
}

Prevention

When it happens

Trigger: setOrderVector(array('title')) where title is a non-unique string column; ordering only by a non-unique status/date column; appending a tiebreaker after a unique column is impossible (that is error 1473), so the unique column must come last.

Common situations: Developers sorting by name, priority, or status and forgetting the id tiebreaker; custom orderable columns declared without 'unique' => true used as the final component.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/adcee070f48bfc16. Report an issue: GitHub.