phacility/phabricator · error · Exception

Diff "%s" does not exist!

Error message

Diff "%s" does not exist!

What it means

DifferentialTransactionEditor::requireDiff() loads a DifferentialDiff by PHID through DifferentialDiffQuery (with the editor's actor as viewer, optionally loading changesets) and throws when the query returns nothing. It fires during revision transaction application whenever a transaction references a diff PHID the actor cannot see or that no longer exists. Typical producers are 'differential.revision.edit' Conduit calls and internal code that applies DifferentialRevisionTransaction TYPE_UPDATE style transactions.

Source

Thrown at src/applications/differential/editor/DifferentialTransactionEditor.php:1020

    $body->addHTMLSection($header, $section_html);
  }

  private function loadDiff($phid, $need_changesets = false) {
    $query = id(new DifferentialDiffQuery())
      ->withPHIDs(array($phid))
      ->setViewer($this->getActor());

    if ($need_changesets) {
      $query->needChangesets(true);
    }

    return $query->executeOne();
  }

  public function requireDiff($phid, $need_changesets = false) {
    $diff = $this->loadDiff($phid, $need_changesets);
    if (!$diff) {
      throw new Exception(pht('Diff "%s" does not exist!', $phid));
    }

    return $diff;
  }

/* -(  Herald Integration  )------------------------------------------------- */

  protected function shouldApplyHeraldRules(
    PhabricatorLiskDAO $object,
    array $xactions) {
    return true;
  }

  protected function didApplyHeraldRules(
    PhabricatorLiskDAO $object,
    HeraldAdapter $adapter,
    HeraldTranscript $transcript) {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Validate the PHID prefix is 'PHID-DIFF-' and load it first with DifferentialDiffQuery using the same viewer before applying transactions
  2. If automating updates, resolve the target revision's current active diff via DifferentialRevisionQuery->needActiveDiffs(true) instead of caching a PHID
  3. Check that the acting user has view permission on the diff's repository and revision (policies are enforced because the query is viewer-scoped)
  4. If the diff was legitimately deleted, drop the stale reference from your transaction set or create a new diff first

Example fix

// before
$editor->applyTransactions($revision, array(
  id(new DifferentialRevisionTransaction())
    ->setTransactionType(DifferentialTransactionType::TYPE_UPDATE)
    ->setNewValue($stale_diff_phid),
));

// after
$diff = id(new DifferentialDiffQuery())
  ->setViewer($actor)
  ->withPHIDs(array($stale_diff_phid))
  ->executeOne();
if (!$diff) {
  $revision_x = id(new DifferentialRevisionQuery())
    ->setViewer($actor)
    ->withIDs(array($revision->getID()))
    ->needActiveDiffs(true)
    ->executeOne();
  $diff = $revision_x->getActiveDiff();
}
// then use $diff->getPHID() in the transaction
Defensive patterns

Strategy: validation

Validate before calling

// Before applying an update transaction, confirm the diff is visible:
$diff = id(new DifferentialDiffQuery())
  ->setViewer($actor)
  ->withPHIDs(array($diff_phid))
  ->executeOne();
if (!$diff) {
  // do not call the editor with this PHID; resolve the active diff instead
  return; 
}

Type guard

function isDiffPHID($phid) {
  return is_string($phid) && preg_match('/^PHID-DIFF-/', $phid);
}

Try / catch

try {
  $diff = $editor->requireDiff($diff_phid);
} catch (Exception $ex) {
  // Treat as stale reference: re-resolve the revision's active diff
  // and skip or rebuild the transaction instead of failing the batch.
  $diff = null;
}

Prevention

When it happens

Trigger: Applying an update transaction with a diff PHID that was deleted or belongs to another install; passing a malformed PHID (e.g., a revision PHID 'PHID-DREV-...' instead of 'PHID-DIFF-...'); the acting user lacking view policy on the repository/revision that owns the diff; automation reusing a cached diff PHID after the diff was destroyed.

Common situations: Scripts or bots that cache PHIDs across runs; Conduit clients that copy data between Phabricator instances; imports where the diff was created but the transaction batch references a stale identifier; policy-restricted repositories making the diff invisible to the acting user.

Related errors


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