phacility/phabricator · warning · PhabricatorApplicationTransactionNoEffectException

Transactions have no effect:

Error message

Transactions have no effect:

What it means

PhabricatorApplicationTransactionNoEffectException is thrown when every transaction in the group would leave the object unchanged (new value equals old value, no comments added). The editor raises it unless setContinueOnNoEffect(true) or setIsPreview(true) is set, so callers notice accidental no-op edits.

Source

Thrown at src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php:2824

    // group, these transactions are junk.
    if (count($meta_xactions) == count($xactions)) {
      $no_effect = $xactions;
      $any_effect = false;
    }

    if (!$no_effect) {
      return $xactions;
    }

    // If none of the transactions have an effect, the meta-transactions also
    // have no effect. Add them to the "no effect" list so we get a full set
    // of errors for everything.
    if (!$any_effect && !$has_comment) {
      $no_effect += $meta_xactions;
    }

    if (!$this->getContinueOnNoEffect() && !$this->getIsPreview()) {
      throw new PhabricatorApplicationTransactionNoEffectException(
        $no_effect,
        $any_effect,
        $has_comment);
    }

    if (!$any_effect && !$has_comment) {
      // If we only have empty comment transactions, just drop them all.
      return array();
    }

    foreach ($no_effect as $key => $xaction) {
      if ($xaction->hasComment()) {
        $xaction->setTransactionType($type_comment);
        $xaction->setOldValue(null);
        $xaction->setNewValue(null);
      } else {
        unset($xactions[$key]);
      }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Filter out no-op transactions before applying: skip any whose getNewValue() equals getOldValue() and that carry no comment.
  2. Call setContinueOnNoEffect(true) on the editor when a partial no-op is acceptable (this is what edit forms do).
  3. Catch PhabricatorApplicationTransactionNoEffectException and use getTransactions()/hasAnyEffect() to decide whether to drop the group or continue.
  4. For previews, setIsPreview(true).

Example fix

// before
$editor->applyTransactions($object, $xactions);

// after
$editor->setContinueOnNoEffect(true)
  ->applyTransactions($object, $xactions);
Defensive patterns

Strategy: try-catch

Validate before calling

// Drop no-op transactions before applying
$xactions = array_filter($xactions, function ($xaction) {
  if ($xaction->hasComment()) {
    return true;
  }
  return $xaction->getNewValue() != $xaction->getOldValue();
});
if (!$xactions) {
  return; // nothing to do
}

Try / catch

try {
  $editor->applyTransactions($object, $xactions);
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
  // Options: drop the no-effect group, or continue past it
  if (!$ex->hasAnyEffect() && !$ex->hasComment()) {
    return; // pure no-op, ignore
  }
  $xactions = $ex->getTransactions();
  $editor->setContinueOnNoEffect(true)
    ->applyTransactions($object, $xactions);
}

Prevention

When it happens

Trigger: Calling applyTransactions() with a transaction whose new value already equals the stored value (e.g. setting a task's owner to its current owner, or adding a subscriber who is already subscribed) while the editor has neither ContinueOnNoEffect nor preview mode enabled.

Common situations: Daemons or import scripts that replay state without first checking current values; Conduit callers submitting full-form transactions where nothing changed; UI code that forgets to setContinueOnNoEffect(true) when the edit form always submits all fields.

Related errors


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