phacility/phabricator · error · ConduitException
ERR_BAD_DIFF
ERR_BAD_DIFF
Error message
ERR_BAD_DIFF
What it means
differential.updaterevision first loads the diff identified by the diffid parameter. If no visible diff with that ID exists — nonexistent, deleted, or hidden by policy from the viewer — the method throws ERR_BAD_DIFF before it touches the revision.
Source
Thrown at src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php:54
protected function defineErrorTypes() {
return array(
'ERR_BAD_DIFF' => pht('Bad diff ID.'),
'ERR_BAD_REVISION' => pht('Bad revision ID.'),
'ERR_WRONG_USER' => pht('You are not the author of this revision.'),
'ERR_CLOSED' => pht('This revision has already been closed.'),
);
}
protected function execute(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$diff = id(new DifferentialDiffQuery())
->setViewer($viewer)
->withIDs(array($request->getValue('diffid')))
->executeOne();
if (!$diff) {
throw new ConduitException('ERR_BAD_DIFF');
}
$revision = id(new DifferentialRevisionQuery())
->setViewer($request->getUser())
->withIDs(array($request->getValue('id')))
->needReviewers(true)
->needActiveDiffs(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$revision) {
throw new ConduitException('ERR_BAD_REVISION');
}
if ($revision->isPublished()) {View on GitHub (pinned to 5720a38cfe)
Solutions
- Send the numeric diffid exactly as returned by differential.creatediff in the same session and under the same user.
- Verify the diff exists (differential.diff.search, or load the revision's diffs) before updating.
- Ensure the user who created the diff is the user who performs the update.
Example fix
// before
$client->callMethodSynchronous('differential.updaterevision', array(
'id' => $revision_id,
'diffid' => $diff_id, // $diff_id may be null or from another instance
));
// after
if ($diff_id === null || $diff_id <= 0) {
throw new InvalidArgumentException('diffid must be a positive integer diff ID.');
}
$client->callMethodSynchronous('differential.updaterevision', array(
'id' => $revision_id,
'diffid' => $diff_id,
)); Defensive patterns
Strategy: validation
Validate before calling
if ($diff_id === null || $diff_id <= 0) {
throw new InvalidArgumentException(
'diffid must be a positive integer diff ID.');
}
// prefer: $diff_id taken directly from the creatediff response in this run Type guard
function isDiffID($value) {
return is_int($value) && $value > 0;
} Try / catch
try {
$result = $client->callMethodSynchronous(
'differential.updaterevision', $params);
} catch (ConduitClientException $ex) {
if ($ex->getErrorCode() === 'ERR_BAD_DIFF') {
// diff missing or invisible: recreate the diff, then retry
} else {
throw $ex;
}
} Prevention
- Create the diff and update the revision with the same user/token in one flow.
- Do not cache diff ids across instances or sessions.
- Treat ERR_BAD_DIFF as permanent for that diff id; recreate the diff instead of retrying.
When it happens
Trigger: diffid is null or unset, refers to a diff on another instance, points at a deleted diff, or the acting user cannot see the diff because of policies.
Common situations: Multi-step arc-style flows where creatediff and updaterevision run under different users or tokens; retrying an update after the diff was removed; IDs copy-pasted between instances.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- ERR_NOT_FOUND
- ERR_BAD_REVISION
- Unsupported action "%s".
- Field "changes" must be non-empty.
- ERR-BAD-REVISION
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/01a4b270c1710777.
Report an issue: GitHub.