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
- Validate the PHID prefix is 'PHID-DIFF-' and load it first with DifferentialDiffQuery using the same viewer before applying transactions
- If automating updates, resolve the target revision's current active diff via DifferentialRevisionQuery->needActiveDiffs(true) instead of caching a PHID
- Check that the acting user has view permission on the diff's repository and revision (policies are enforced because the query is viewer-scoped)
- 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
- Never cache diff PHIDs across runs; resolve the revision's active diff with needActiveDiffs(true) at call time
- Validate PHID prefixes (PHID-DIFF-) before constructing transactions
- Run lookups with the same viewer that will apply the transactions so policy failures surface early
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.