phacility/phabricator · error · Exception

Specify exactly one of '%s' and '%s'.

Error message

Specify exactly one of '%s' and '%s'.

What it means

The frozen Conduit method 'maniphest.update' requires the caller to identify the target task with exactly one of the parameters 'id' or 'phid'. The guard in execute() throws a plain Exception when both are supplied at once or when neither is, because the method cannot decide which task to load.

Source

Thrown at src/applications/maniphest/conduit/ManiphestUpdateConduitAPIMethod.php:44

      'ERR-INVALID-PARAMETER' => pht('Missing or malformed parameter.'),
      'ERR-NO-EFFECT'         => pht('Update has no effect.'),
    );
  }

  protected function defineParamTypes() {
    return $this->getTaskFields($is_new = false);
  }

  protected function defineReturnType() {
    return 'nonempty dict';
  }

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

    if (($id && $phid) || (!$id && !$phid)) {
      throw new Exception(
        pht(
          "Specify exactly one of '%s' and '%s'.",
          'id',
          'phid'));
    }

    $query = id(new ManiphestTaskQuery())
      ->setViewer($request->getUser())
      ->needSubscriberPHIDs(true)
      ->needProjectPHIDs(true);
    if ($id) {
      $query->withIDs(array($id));
    } else {
      $query->withPHIDs(array($phid));
    }
    $task = $query->executeOne();

    $params = $request->getAllParameters();

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Pass exactly one identifier: use 'id' => $task_id or 'phid' => $task_phid, never both.
  2. If the identifier came from a failed lookup, fix the upstream lookup so id/phid is always a real non-empty value before calling update.
  3. Long term, migrate to maniphest.edit, which takes a single 'object' identifier and replaces this frozen method.

Example fix

// before
$params = array(
  'id'   => $task_id,
  'phid' => $task_phid, // both set -> throws
  'title' => 'New title',
);

// after
$params = array(
  'phid' => $task_phid,
  'title' => 'New title',
);
Defensive patterns

Strategy: validation

Validate before calling

$has_id  = !empty($params['id']);
$has_phid = !empty($params['phid']);
if ($has_id === $has_phid) { // both or neither
  throw new InvalidArgumentException(
    'Pass exactly one of id or phid to maniphest.update'
  );
}

Try / catch

try {
  $result = $conduit->callMethodSynchronous('maniphest.update', $params);
} catch (Exception $e) {
  if (strpos($e->getMessage(), "exactly one of 'id' and 'phid'") !== false) {
    // fix caller-supplied identifier and retry once
  } else {
    throw $e;
  }
}

Prevention

When it happens

Trigger: Calling maniphest.update with both 'id' and 'phid' set (id && phid), or with neither set (!$id && !$phid). Example bad payloads: array('id' => 123, 'phid' => 'PHID-TASK-...', 'title' => 'x') or array('title' => 'x') with no identifier at all.

Common situations: Wrapper scripts that build a params dict and unconditionally merge in both identifiers 'to be safe', or code paths where the id/phid variable is empty due to an upstream lookup failure (e.g. the task was looked up by a previous call that returned nothing). Copy-pasting a maniphest.create response (which returns a phid) into an update template that already has an id field.

Related errors


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