phacility/phabricator · error · ConduitException

ERR_BAD_REVISION

ERR_BAD_REVISION

Error message

ERR_BAD_REVISION

What it means

Thrown by the differential.createcomment Conduit method. It loads the revision from the revision_id parameter with a policy-filtered query that also attaches reviewers, reviewer authority, and active diffs. If no revision is visible for that ID, it throws ConduitException('ERR_BAD_REVISION') before building any comment or status-change transaction.

Source

Thrown at src/applications/differential/conduit/DifferentialCreateCommentConduitAPIMethod.php:55

  protected function defineErrorTypes() {
    return array(
      'ERR_BAD_REVISION' => pht('Bad revision ID.'),
    );
  }

  protected function execute(ConduitAPIRequest $request) {
    $viewer = $request->getUser();

    $revision = id(new DifferentialRevisionQuery())
      ->setViewer($viewer)
      ->withIDs(array($request->getValue('revision_id')))
      ->needReviewers(true)
      ->needReviewerAuthority(true)
      ->needActiveDiffs(true)
      ->executeOne();
    if (!$revision) {
      throw new ConduitException('ERR_BAD_REVISION');
    }

    $xactions = array();

    $modular_map = array(
      'accept' => DifferentialRevisionAcceptTransaction::TRANSACTIONTYPE,
      'reject' => DifferentialRevisionRejectTransaction::TRANSACTIONTYPE,
      'resign' => DifferentialRevisionResignTransaction::TRANSACTIONTYPE,
      'request_review' =>
        DifferentialRevisionRequestReviewTransaction::TRANSACTIONTYPE,
      'rethink' => DifferentialRevisionPlanChangesTransaction::TRANSACTIONTYPE,
    );

    $action = $request->getValue('action');
    if (isset($modular_map[$action])) {
      $xactions[] = id(new DifferentialTransaction())
        ->setTransactionType($modular_map[$action])
        ->setNewValue(true);

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Pass revision_id as a bare integer that you confirmed exists (differential.query with ids=[id])
  2. Run the call with a conduit token whose user can see the revision's project/space policies
  3. If the revision may have been deleted, catch ERR_BAD_REVISION and skip instead of retrying
  4. Prefer the newer differential.revision.edit method with a comment transaction for new integrations

Example fix

// before
$params = array(
  'revision_id' => 'D456',
  'message' => 'Ship it.',
);

// after
$params = array(
  'revision_id' => 456,
  'message' => 'Ship it.',
);
Defensive patterns

Strategy: try-catch

Validate before calling

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

Try / catch

try {
  $client->callMethodSynchronous('differential.createcomment', $params);
} catch (ConduitClientException $ex) {
  if ($ex->getErrorCode() === 'ERR_BAD_REVISION') {
    // bad or invisible revision_id: fix input, do not retry as-is
  } else {
    throw $ex;
  }
}

Prevention

When it happens

Trigger: Calling differential.createcomment with revision_id equal to 0, null, a nonexistent ID, a 'D'-prefixed string, or an ID whose revision the acting user cannot see under visibility policies.

Common situations: CI bots posting review feedback on revisions in restricted spaces; scripts reusing IDs from a stale tracking database; passing a revision PHID where the integer ID is expected; the revision was deleted or moved between the lookup and the comment call.

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/81e62eb98c53a0dc. Report an issue: GitHub.