phacility/phabricator · error · Exception
You must call %s when rendering an %s.
Error message
You must call %s when rendering an %s.
What it means
Modern Phabricator views require an acting viewer (the logged-in PhabricatorUser) before rendering, because policies, links, and rendering all depend on who is looking. AphrontDialogView::render() enforces this by checking hasViewer() and throwing if render() is reached without a prior setViewer() call. It is a programming error in the calling controller or view, not a runtime data problem.
Source
Thrown at src/view/AphrontDialogView.php:295
$meta = array();
if ($this->disableWorkflowOnCancel) {
$meta['disableWorkflow'] = true;
}
$buttons[] = javelin_tag(
'a',
array(
'href' => $this->cancelURI,
'class' => 'button button-grey',
'name' => '__cancel__',
'sigil' => 'jx-workflow-button',
'meta' => $meta,
),
$this->cancelText);
}
if (!$this->hasViewer()) {
throw new Exception(
pht(
'You must call %s when rendering an %s.',
'setViewer()',
__CLASS__));
}
$classes = array();
$classes[] = 'aphront-dialog-view';
$classes[] = $this->class;
if ($this->flush) {
$classes[] = 'aphront-dialog-flush';
}
switch ($this->width) {
case self::WIDTH_FORM:
case self::WIDTH_FULL:
$classes[] = 'aphront-dialog-view-width-'.$this->width;
break;View on GitHub (pinned to 5720a38cfe)
Solutions
- Call '$dialog->setViewer($this->getViewer())' (in a PhabricatorController) or '$dialog->setViewer($request->getViewer())' before producing the response.
- If the dialog is built in a helper, pass the viewer in and set it there, so every construction path is covered.
- Set the viewer immediately after constructing the view so later refactors cannot drop it.
- Search for other view constructions in the same code path — the same omission usually repeats.
Example fix
// before
public function handleRequest(AphrontRequest $request) {
$dialog = id(new AphrontDialogView())
->setTitle(pht('Delete widget'))
->addCancelButton('/w/');
return id(new AphrontResponse())->setContent($dialog->render());
}
// after
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$dialog = id(new AphrontDialogView())
->setViewer($viewer)
->setTitle(pht('Delete widget'))
->addCancelButton('/w/');
return id(new AphrontResponse())->setContent($dialog->render());
} Defensive patterns
Strategy: validation
Validate before calling
// Always resolve and set the viewer as the first step of dialog construction: $viewer = $request->getViewer(); // or $this->getViewer() inside a controller $dialog = id(new AphrontDialogView()) ->setViewer($viewer) // set immediately, before any content ->setTitle($title);
Prevention
- Make setViewer() the first call in every view-construction chain so refactors cannot drop it.
- In helpers that build dialogs, require the viewer as a parameter instead of constructing views viewer-less.
- When porting old extensions, search for 'new AphrontDialogView()' and add setViewer() to each site.
When it happens
Trigger: Building an AphrontDialogView in a controller/handler and returning it (or calling render()) without '$dialog->setViewer($viewer)' first. Typical with ported pre-viewer-era code, dialogs created inside helper methods that never receive the viewer, or dialogs constructed in CLI/AJAX contexts where no viewer was resolved from the request.
Common situations: Upgrading old Phabricator code or extensions written before views required viewers; copy-pasting dialog code from another controller and dropping the setViewer line; rendering a dialog from a workflow handler where $request->getViewer() was never fetched.
Related errors
- Unknown field property view style '%s'; valid styles are '%s
- Unknown dialog width '%s'!
- Service "%s" is unrecognized, restricted, or you do not have
- When creating a new Almanac interface via the Conduit API, y
- Device "%s" is unrecognized, restricted, or you do not have
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/da294901e297f445.
Report an issue: GitHub.