phacility/phabricator · error · ConduitException

ERR_CLOSED

ERR_CLOSED

Error message

ERR_CLOSED

What it means

Once a revision isPublished() — its change landed and the revision was closed — differential.updaterevision refuses further edits and throws ERR_CLOSED before applying any field edit. In Differential's model a closed revision is a historical record; new work requires a new revision.

Source

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

    }

    $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. Create a new revision for follow-up changes instead of updating the closed one.
  2. If the close itself was wrong, reopen the revision first (the revision 'Reopen' command/action) and retry.
  3. Guard the call: load the revision and skip when its status is published/closed.

Example fix

// before
$client->callMethodSynchronous('differential.updaterevision', $params);

// after
$result = $client->callMethodSynchronous('differential.revision.search', array(
  'constraints' => array('ids' => array($revision_id)),
));
$status = idx(idx(idx($result, 'data', array()), 0, array()), 'fields', array());
if (idx(idx($status, 'status', array()), 'value') === 'published') {
  // closed: create a new revision instead
  return;
}
$client->callMethodSynchronous('differential.updaterevision', $params);
Defensive patterns

Strategy: validation

Validate before calling

$result = $client->callMethodSynchronous('differential.revision.search', array(
  'constraints' => array('ids' => array($revision_id)),
));
$revision = idx(idx($result, 'data', array()), 0, array());
$status_value = idx(idx(idx($revision, 'fields', array()), 'status', array()), 'value');
if ($status_value === 'published') {
  // closed: create a new revision instead of updating
  return;
}

Try / catch

try {
  $result = $client->callMethodSynchronous(
    'differential.updaterevision', $params);
} catch (ConduitClientException $ex) {
  if ($ex->getErrorCode() === 'ERR_CLOSED') {
    // permanent for this revision: create a new revision, do not retry
  } else {
    throw $ex;
  }
}

Prevention

When it happens

Trigger: Calling differential.updaterevision on a revision that was closed by arc land, by commit detection, or by a manual close action.

Common situations: Automation retrying an update after the branch already landed; developers who keep working on a topic after landing and push updates to the old revision.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/defa317440f608f2. Report an issue: GitHub.