phacility/phabricator · warning · PhabricatorSystemActionRateLimitException
You are performing too many actions too quickly.
Error message
You are performing too many actions too quickly.
What it means
PhabricatorSystemActionEngine::willTakeAction() implements Phabricator's flood control: before an action executes it loads each actor's accumulated score for that PhabricatorSystemAction and, if the actor is already blocked, throws PhabricatorSystemActionRateLimitException. Scores decay over time, so the block is temporary and self-clearing.
Source
Thrown at src/applications/system/engine/PhabricatorSystemActionEngine.php:52
* @{class:PhabricatorSystemActionRateLimitException}.
*
* @param list<string> List of actors.
* @param PhabricatorSystemAction Action being taken.
* @param float Score or credit, see above.
* @return void
*/
public static function willTakeAction(
array $actors,
PhabricatorSystemAction $action,
$score) {
// If the score for this action is negative, we're giving the user a credit,
// so don't bother checking if they're blocked or not.
if ($score >= 0) {
$blocked = self::loadBlockedActors($actors, $action, $score);
if ($blocked) {
foreach ($blocked as $actor => $actor_score) {
throw new PhabricatorSystemActionRateLimitException(
$action,
$actor_score);
}
}
}
if ($score != 0) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
self::recordAction($actors, $action, $score);
unset($unguarded);
}
}
public static function loadBlockedActors(
array $actors,
PhabricatorSystemAction $action,
$score) {
View on GitHub (pinned to 5720a38cfe)
Solutions
- Slow down or pause the loop so scores decay below the limit, then resume
- Raise (or disable) the specific action limit in Config > Actions
- Batch work so a single action covers many objects instead of one action per object
- Run flood-heavy scripted work under a dedicated actor with its own score budget
Example fix
// before: one rate-limited action per row, no guard
foreach ($rows as $row) {
PhabricatorSystemActionEngine::willTakeAction(
array($actor_phid), new ExampleAction(), 1.0);
do_work($row);
}
// after: back off and retry when the actor is throttled
foreach ($rows as $row) {
while (true) {
try {
PhabricatorSystemActionEngine::willTakeAction(
array($actor_phid), new ExampleAction(), 1.0);
break;
} catch (PhabricatorSystemActionRateLimitException $ex) {
sleep(60); // wait for scores to decay, then retry this row
}
}
do_work($row);
} Defensive patterns
Strategy: retry
Try / catch
Wrap only the willTakeAction() call and catch PhabricatorSystemActionRateLimitException specifically (not Exception); read the exception's action/score, wait out the decay window with a sleep of tens of seconds, then retry the same item. Never widen the catch: real failures must still surface.
Prevention
- Throttle loops that fire a rate-limited action per row
- Tune action limits in Config > Actions to match legitimate bulk volume
- Spread bursts through queue workers so actions occur over time
- Give scripted actors their own account so floods do not block humans
When it happens
Trigger: Calling willTakeAction() with a positive score for an actor whose recent score for that action exceeds the configured limit: bulk mail/mention/token actions, account creation bursts, or any per-row scripted action whose thresholds (Config > Actions) are set low relative to script volume.
Common situations: Scripts iterating thousands of records and firing one rate-limited action per row; a lowered threshold in PhabricatorSystemActionConfig; a shared bot actor whose budget is consumed by normal traffic plus scripted traffic.
Related errors
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/f3a9b2de3d0e6371.
Report an issue: GitHub.