phacility/phabricator · error · ConduitException

ERR_BAD_REVISION

ERR_BAD_REVISION

Error message

ERR_BAD_REVISION

What it means

The revision referenced by id must exist and the acting user must hold both CAN_VIEW and CAN_EDIT on it (the query calls requireCapabilities). Nonexistent, invisible, and non-editable revisions all surface as ERR_BAD_REVISION, so this single code covers both not-found and permission failure.

Source

Thrown at src/applications/differential/conduit/DifferentialUpdateRevisionConduitAPIMethod.php:69

      ->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()) {
      throw new ConduitException('ERR_CLOSED');
    }

    $this->applyFieldEdit(
      $request,
      $revision,
      $diff,
      $request->getValue('fields', array()),
      $request->getValue('message'));

    return array(
      'revisionid'  => $revision->getID(),
      'uri'         => PhabricatorEnv::getURI('/D'.$revision->getID()),
    );
  }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Confirm the numeric revision id (no 'D' prefix).
  2. Ensure the conduit user is the revision author or otherwise has edit rights.
  3. Pre-check visibility with differential.revision.search as the same user; an invisible revision fails the same way.
  4. Check you are on the instance that owns the revision.

Example fix

// before: update runs as a bot that cannot edit the revision
$client->callMethodSynchronous('differential.updaterevision', array(
  'id' => 123,
  'diffid' => 456,
));

// after: run as the revision author's token, numeric ids only
$client->callMethodSynchronous('differential.updaterevision', array(
  'id' => 123,
  'diffid' => 456,
));
Defensive patterns

Strategy: validation

Validate before calling

$result = $client->callMethodSynchronous('differential.revision.search', array(
  'constraints' => array('ids' => array($revision_id)),
));
if (empty($result['data'])) {
  throw new RuntimeException(
    'Revision missing or not visible to this user.');
}

Type guard

function isRevisionID($value) {
  return is_int($value) && $value > 0;
}

Try / catch

try {
  $result = $client->callMethodSynchronous(
    'differential.updaterevision', $params);
} catch (ConduitClientException $ex) {
  if ($ex->getErrorCode() === 'ERR_BAD_REVISION') {
    // not found or no edit rights: re-check id, author, and policies
  } else {
    throw $ex;
  }
}

Prevention

When it happens

Trigger: id is wrong or nonexistent; the user is not the author and has no edit rights; the view policy hides the revision so the policy-filtered query returns null; the ID is prefixed with 'D'.

Common situations: Bots or teammates trying to update someone else's revision; a revision moved under a restrictive project policy; ID mix-ups across instances; automation running with the wrong token.

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/57c6c1a0f1a31b78. Report an issue: GitHub.