phacility/phabricator · error · Exception

Failed to create a new ref cursor (for "%s", of type "%s", i

Error message

Failed to create a new ref cursor (for "%s", of type "%s", in repository "%s") because it collided with an existing cursor, but then failed to load that cursor.

What it means

Thrown in PhabricatorRepositoryRefEngine when creating a ref cursor: the INSERT hit a duplicate-key exception (AphrontDuplicateKeyQueryException), meaning another daemon won the race to create the same cursor, but the follow-up query that loads the winning cursor by repository PHID, ref type, and ref name returned nothing. So the cursor row exists yet cannot be reloaded, usually because it was deleted between the failed insert and the load, or the query filters (such as the active positions requirement) exclude it.

Source

Thrown at src/applications/repository/engine/PhabricatorRepositoryRefEngine.php:643

    try {
      return $cursor->save();
    } catch (AphrontDuplicateKeyQueryException $ex) {
      // If we raced another daemon to create this position and lost the race,
      // load the cursor the other daemon created instead.
    }

    $viewer = $this->getViewer();

    $cursor = id(new PhabricatorRepositoryRefCursorQuery())
      ->setViewer($viewer)
      ->withRepositoryPHIDs(array($repository->getPHID()))
      ->withRefTypes(array($ref_type))
      ->withRefNames(array($ref_name))
      ->needPositions(true)
      ->executeOne();
    if (!$cursor) {
      throw new Exception(
        pht(
          'Failed to create a new ref cursor (for "%s", of type "%s", in '.
          'repository "%s") because it collided with an existing cursor, '.
          'but then failed to load that cursor.',
          $ref_name,
          $ref_type,
          $repository->getDisplayName()));
    }

    return $cursor;
  }

  private function saveNewPositions() {
    $positions = $this->newPositions;

    foreach ($positions as $position) {
      try {
        $position->save();

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Treat as transient first: re-run 'bin/repository refs <repository>' or let the daemon retry the ref update task
  2. If persistent, query the ref cursor table for the named ref to see whether a row actually exists
  3. If rows exist but are not loadable, prune orphaned or broken cursors for that ref and let the engine recreate them
  4. If it recurs constantly in a cluster, reduce ref-update concurrency by binding fewer devices to the repository service
Defensive patterns

Strategy: retry

Try / catch

try {
  $cursor = $engine->updateRefCursor($repository, $ref_type, $ref_name);
} catch (Exception $ex) {
  if (preg_match('/failed to load that cursor/', $ex->getMessage())) {
    // lost the insert race and the winner vanished; retry recreates cleanly
    $cursor = $engine->updateRefCursor($repository, $ref_type, $ref_name);
  }
}

Prevention

When it happens

Trigger: Two daemons racing to record the same ref update in a cluster; a ref deleted immediately after being created during rapid force-push sequences; cursor cleanup transactions deleting the cursor concurrently; the load filtering out a cursor whose positions were pruned.

Common situations: High-frequency force-pushing while multiple ref tasks run; cluster deployments with multiple repository devices; rapid branch create-and-delete cycles in CI automation.

Related errors


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