phacility/phabricator · error · ConduitException
ERR_BAD_DIFF
ERR_BAD_DIFF
Error message
ERR_BAD_DIFF
What it means
Thrown by the differential.createrevision Conduit method. It loads the diff identified by the diffid parameter with a viewer-policy-filtered DifferentialDiffQuery; if no diff is found, ConduitException('ERR_BAD_DIFF') ('Bad diff ID.') is thrown before the new revision is initialized and field edit applied.
Source
Thrown at src/applications/differential/conduit/DifferentialCreateRevisionConduitAPIMethod.php:52
protected function defineReturnType() {
return 'nonempty dict';
}
protected function defineErrorTypes() {
return array(
'ERR_BAD_DIFF' => pht('Bad diff ID.'),
);
}
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 = DifferentialRevision::initializeNewRevision($viewer);
$revision->attachReviewers(array());
$result = $this->applyFieldEdit(
$request,
$revision,
$diff,
$request->getValue('fields', array()),
$message = null);
$revision_id = $result['object']['id'];
return array(
'revisionid' => $revision_id,
'uri' => PhabricatorEnv::getURI('/D'.$revision_id),
);View on GitHub (pinned to 5720a38cfe)
Solutions
- Use exactly the 'diffid' value returned by your earlier differential.creatediff call
- Check that the creatediff call succeeded before proceeding - abort the script on any error there
- Sanity-check the ID with differential.getdiff before creating the revision
- Cast diffid to int; never send a 'D'-prefixed value
Example fix
// before
$result = $client->callMethodSynchronous('differential.creatediff', $params);
// creatediff failed earlier; $diffid = 0 from a fallback
$client->callMethodSynchronous('differential.createrevision', array(
'diffid' => $diffid,
));
// after
$result = $client->callMethodSynchronous('differential.creatediff', $params);
$diffid = (int)$result['diffid']; // creatediff returns the real id
$client->callMethodSynchronous('differential.createrevision', array(
'diffid' => $diffid,
'fields' => $fields,
)); Defensive patterns
Strategy: validation
Validate before calling
// Pipe the creatediff result; abort if it failed.
$create = $client->callMethodSynchronous('differential.creatediff', $diff_params);
$diffid = (int)idx($create, 'diffid', 0);
if ($diffid <= 0) {
throw new RuntimeException('creatediff did not return a diffid');
} Try / catch
try {
$client->callMethodSynchronous('differential.createrevision',
array('diffid' => $diffid, 'fields' => $fields));
} catch (ConduitClientException $ex) {
if ($ex->getErrorCode() === 'ERR_BAD_DIFF') {
// diffid invalid: recreate the diff before retrying
} else {
throw $ex;
}
} Prevention
- Chain createrevision directly to the creatediff response
- Fail fast when creatediff errors; never substitute a guessed ID
- Distinguish revision IDs from diff IDs in variable naming
When it happens
Trigger: Calling differential.createrevision with a diffid that was never created, belongs to an invisible diff, is a revision ID by mistake, or comes from a failed differential.creatediff step whose error the script ignored.
Common situations: Two-step scripts (creatediff then createrevision) that continue after the first step failed; passing the revision number instead of the diff id; diff IDs carried over from a staging install to production.
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
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/7fdd123619d1cc2f.
Report an issue: GitHub.