phacility/phabricator · error · Exception
Event queries which generate ghost events must include eithe
Error message
Event queries which generate ghost events must include either a result limit or an end date, because they may otherwise generate an infinite number of results. This query has neither.
What it means
PhabricatorCalendarEventQuery throws when setGenerateGhosts(true) is used without either a result limit (setLimit) or an end date (withDateRange). Ghost generation materializes every occurrence of every recurring event matching the query, which is unbounded without one of those caps, so the query refuses rather than loop forever.
Source
Thrown at src/applications/calendar/query/PhabricatorCalendarEventQuery.php:175
return true;
}
protected function loadPage() {
$events = $this->loadStandardPage($this->newResultObject());
$viewer = $this->getViewer();
foreach ($events as $event) {
$event->applyViewerTimezone($viewer);
}
if (!$this->generateGhosts) {
return $events;
}
$raw_limit = $this->getRawResultLimit();
if (!$raw_limit && !$this->rangeEnd) {
throw new Exception(
pht(
'Event queries which generate ghost events must include either a '.
'result limit or an end date, because they may otherwise generate '.
'an infinite number of results. This query has neither.'));
}
foreach ($events as $key => $event) {
$sequence_start = 0;
$sequence_end = null;
$end = null;
$instance_of = $event->getInstanceOfEventPHID();
if ($instance_of == null && $this->isCancelled !== null) {
if ($event->getIsCancelled() != $this->isCancelled) {
unset($events[$key]);
continue;
}View on GitHub (pinned to 5720a38cfe)
Solutions
- Add an explicit result cap: ->setLimit(1000)
- Add a range end: ->withDateRange($start_epoch, $end_epoch) (an end bound is required; a start-only range is not enough)
- For full exports, loop with paging: keep a limit per page and advance rangeBegin until exhausted
Example fix
// before $events = id(new PhabricatorCalendarEventQuery()) ->setViewer($viewer) ->setGenerateGhosts(true) ->execute(); // after $events = id(new PhabricatorCalendarEventQuery()) ->setViewer($viewer) ->setGenerateGhosts(true) ->withDateRange($start_epoch, $end_epoch) ->setLimit(1000) ->execute();
Defensive patterns
Strategy: validation
Validate before calling
if ($generate_ghosts && !$limit && !$end_epoch) {
throw new Exception(
'Ghost generation requires a result limit or a range end date.');
}
$events = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->setGenerateGhosts($generate_ghosts)
->withDateRange($start_epoch, $end_epoch)
->setLimit($limit ?: 1000)
->execute(); Try / catch
try {
$events = $query->execute();
} catch (Exception $ex) {
if (strpos($ex->getMessage(), 'infinite number of results') !== false) {
$query->setLimit(1000);
$events = $query->execute();
} else {
throw $ex;
}
} Prevention
- Always pair setGenerateGhosts(true) with withDateRange() or setLimit() by convention
- Remember a start-only range does not satisfy the guard — only rangeEnd or a limit does
- For exports, page through time windows (e.g. month by month) instead of one unbounded query
When it happens
Trigger: id(new PhabricatorCalendarEventQuery())->setViewer($viewer)->setGenerateGhosts(true)->execute() with no setLimit(N) and no withDateRange($start, $end). Even with a rangeBegin only, the query still throws because only rangeEnd counts as the bound.
Common situations: Custom calendar dashboards or export scripts that iterate 'all upcoming instances'; upgrading code that previously worked because a limit was set elsewhere and later removed; search-engine code that forgets to forward the user's rangeEnd filter into the query.
Related errors
- This event instance has not been created yet. Log in to crea
- Unable to load file ("%s") for import.
- Specify at least one import ID to reload.
- Unable to load Calendar import with ID "%s".
- RRULE evaluation failed to generate more events in the next
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/6f209519d25c4eb7.
Report an issue: GitHub.