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

  1. Use exactly the 'diffid' value returned by your earlier differential.creatediff call
  2. Check that the creatediff call succeeded before proceeding - abort the script on any error there
  3. Sanity-check the ID with differential.getdiff before creating the revision
  4. 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

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.