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
- Pass revision_id as a bare integer that you confirmed exists (differential.query with ids=[id])
- Run the call with a conduit token whose user can see the revision's project/space policies
- If the revision may have been deleted, catch ERR_BAD_REVISION and skip instead of retrying
- 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
- Validate revision_id as a positive integer before building params
- Use a token whose user can see the revision's space and policies
- In feedback bots, catch and skip per-revision instead of aborting the batch
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.