phacility/phabricator · error · ConduitException

ERR_BAD_DIFF

ERR_BAD_DIFF

Error message

ERR_BAD_DIFF

What it means

Thrown by the differential.getdiff Conduit method in its revision branch. When revision_id is supplied, the method queries all diffs attached to that revision (withRevisionIDs) and takes the newest; if the query returns no diffs at all - wrong ID, revision invisible to the viewer, or a revision with zero attached diffs - ConduitException('ERR_BAD_DIFF') is thrown before the diff is loaded.

Source

Thrown at src/applications/differential/conduit/DifferentialGetDiffConduitAPIMethod.php:62

      'ERR_BAD_DIFF' => pht('No such diff exists.'),
    );
  }

  protected function execute(ConduitAPIRequest $request) {
    $diff_id = $request->getValue('diff_id');

    // If we have a revision ID, we need the most recent diff. Figure that out
    // without loading all the attached data.
    $revision_id = $request->getValue('revision_id');
    if ($revision_id) {
      $diffs = id(new DifferentialDiffQuery())
        ->setViewer($request->getUser())
        ->withRevisionIDs(array($revision_id))
        ->execute();
      if ($diffs) {
        $diff_id = head($diffs)->getID();
      } else {
        throw new ConduitException('ERR_BAD_DIFF');
      }
    }

    $diff = null;
    if ($diff_id) {
      $diff = id(new DifferentialDiffQuery())
        ->setViewer($request->getUser())
        ->withIDs(array($diff_id))
        ->needChangesets(true)
        ->executeOne();
    }

    if (!$diff) {
      throw new ConduitException('ERR_BAD_DIFF');
    }

    return $diff->getDiffDict();
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Verify the revision exists and is visible via differential.query before calling
  2. If you already know the diff, pass diff_id directly and skip the revision lookup
  3. Send revision_id as a bare integer
  4. Catch ERR_BAD_DIFF distinctly: here it means 'no diffs for revision', not necessarily a bad diff id

Example fix

// before
$params = array('revision_id' => 'D77');

// after
$params = array('revision_id' => 77);
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the revision has diffs before fetching.
$found = $client->callMethodSynchronous('differential.query',
  array('ids' => array((int)$rev_id)));
if (!$found) {
  throw new InvalidArgumentException('Unknown revision '.$rev_id);
}

Try / catch

try {
  $dict = $client->callMethodSynchronous('differential.getdiff',
    array('revision_id' => (int)$rev_id));
} catch (ConduitClientException $ex) {
  if ($ex->getErrorCode() === 'ERR_BAD_DIFF') {
    // revision unknown/invisible or has no diffs
  } else {
    throw $ex;
  }
}

Prevention

When it happens

Trigger: Calling differential.getdiff with a revision_id that does not exist, is 'D'-prefixed, is invisible to the caller, or names a revision that has no diffs yet.

Common situations: Automation fetching the current diff for a revision number parsed from a branch name; restricted spaces hiding the revision; passing a PHID instead of the integer ID.

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/7694532b13e0b287. Report an issue: GitHub.