phacility/phabricator · error · PhabricatorInvalidQueryCursorException

Cursor "%s" does not identify a valid object in query "%s".

Error message

Cursor "%s" does not identify a valid object in query "%s".

What it means

Thrown as PhabricatorInvalidQueryCursorException by cursor-paged policy queries when an external cursor (the 'after' paging token — usually an object ID or PHID) cannot be resolved. The engine re-executes the query constrained to that single object: this both validates the object exists and enforces that the viewer may see it (using an invisible object as a cursor would leak information). If executeOne() returns nothing, the cursor is invalid for this viewer/query combination.

Source

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

    // like a cursor this parent query would generate.
    $query->setOrderVector($this->getOrderVector());

    $this->applyExternalCursorConstraintsToQuery($query, $cursor);

    // If we have a Ferret fulltext query, copy it to the subquery so that we
    // generate ranking columns appropriately, and compute the correct object
    // ranking score for the current query.
    if ($this->ferretEngine) {
      $query->withFerretConstraint($this->ferretEngine, $this->ferretTokens);
    }

    // We're executing the subquery normally to make sure the viewer can
    // actually see the object, and that it's a completely valid object which
    // passes all filtering and policy checks. You aren't allowed to use an
    // object you can't see as a cursor, since this can leak information.
    $result = $query->executeOne();
    if (!$result) {
      $this->throwCursorException(
        pht(
          'Cursor "%s" does not identify a valid object in query "%s".',
          $cursor,
          get_class($this)));
    }

    // Now that we made sure the viewer can actually see the object the
    // external cursor identifies, return the internal cursor the query
    // generated as a side effect while loading the object.
    return $query->getInternalCursorObject();
  }

  final protected function throwCursorException($message) {
    throw new PhabricatorInvalidQueryCursorException($message);
  }

  protected function applyExternalCursorConstraintsToQuery(
    PhabricatorCursorPagedPolicyAwareQuery $subquery,

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Restart pagination from the first page to get a fresh cursor
  2. Make sure the 'after' token comes from the immediately preceding page of the same query with the same filters and the same viewer
  3. If writing an API consumer, treat this exception as 'cursor expired' and transparently re-issue the query from page one

Example fix

// before: blindly paging with a saved cursor
$results = $query->setAfterCursor($saved_cursor)->execute();

// after: recover from an expired/invalid cursor by restarting
try {
  $results = $query->setAfterCursor($cursor)->execute();
} catch (PhabricatorInvalidQueryCursorException $ex) {
  $results = $query->setAfterCursor(null)->execute(); // page 1 again
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Optional pre-check: verify the cursor object is visible before paging.
if ($cursor) {
  $visible = (bool)id(clone $query)
    ->setAfterCursor(null)
    ->withPHIDs(array($cursor))
    ->execute();
  if (!$visible) { $cursor = null; } // restart from page 1
}

Try / catch

try {
  $results = $query->setAfterCursor($cursor)->execute();
} catch (PhabricatorInvalidQueryCursorException $ex) {
  // cursor expired/invalid: restart pagination from the first page
  $results = $query->setAfterCursor(null)->execute();
}

Prevention

When it happens

Trigger: Passing after=<PHID> for an object the viewer cannot see under policy filtering; a cursor referencing a since-deleted object; a cursor from a different query (whose filters exclude the object); stale paging links after the underlying data changed.

Common situations: A user leaves a browser tab open on page N of results, the objects on that page are deleted or restricted, and they click 'next page'; API consumers caching 'after' tokens across query modifications or long time spans.

Related errors


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