phacility/phabricator · error · Exception
Call %s before rendering tokenizers. Use %s on %s to do this
Error message
Call %s before rendering tokenizers. Use %s on %s to do this easily.
What it means
To display existing values as tokens, the tokenizer must resolve PHIDs to handles, which requires a viewer (loadHandles() calls getUser()). The form wires its viewer into controls only at render time and only for controls registered via appendControl() (buildLayoutView() calls setViewer() on $this->controls). A tokenizer added with appendChild(), or rendered standalone, never receives a viewer and throws this exception. The message names the two fixes: setUser() on the control, or appendControl() on AphrontFormView.
Source
Thrown at src/view/form/control/AphrontFormTokenizerControl.php:130
$tokens,
'getAvailabilityColor',
'getKey'),
'limit' => $this->limit,
'username' => $username,
'placeholder' => $placeholder,
'browseURI' => $browse_uri,
'disabled' => $this->getDisabled(),
));
}
return $template->render();
}
private function loadHandles() {
if ($this->handles === null) {
$viewer = $this->getUser();
if (!$viewer) {
throw new Exception(
pht(
'Call %s before rendering tokenizers. '.
'Use %s on %s to do this easily.',
'setUser()',
'appendControl()',
'AphrontFormView'));
}
$values = nonempty($this->getValue(), array());
$phids = array();
foreach ($values as $value) {
if (!PhabricatorTypeaheadDatasource::isFunctionToken($value)) {
$phids[] = $value;
}
}
$this->handles = $viewer->loadHandles($phids);View on GitHub (pinned to 5720a38cfe)
Solutions
- Add the control with $form->appendControl($control) so the form propagates its viewer to it.
- Or set the user explicitly on the control: $control->setUser($viewer) (setViewer() is the non-deprecated equivalent).
- Confirm the parent form itself had setUser() called; otherwise fix that too.
Example fix
// before $form = id(new AphrontFormView()) ->setUser($viewer); $tokenizer = id(new AphrontFormTokenizerControl()) ->setDatasource(new PhabricatorProjectDatasource()); $form->appendChild($tokenizer); // not registered as a control: no viewer wired // after $form = id(new AphrontFormView()) ->setUser($viewer); $tokenizer = id(new AphrontFormTokenizerControl()) ->setDatasource(new PhabricatorProjectDatasource()); $form->appendControl($tokenizer); // form wires viewer in at render time
Defensive patterns
Strategy: validation
Validate before calling
if (!$control->hasViewer()) {
$control->setUser($viewer);
}
$form->appendControl($control); Prevention
- Always add form controls through appendControl(), never appendChild().
- hasViewer() is public on every AphrontView — assert it for tokenizer/typeahead controls before render.
- Standalone tokenizers need setUser($viewer) before any render or willRender() pass.
When it happens
Trigger: $form->appendChild($tokenizer) instead of $form->appendControl($tokenizer); or instantiating an AphrontFormTokenizerControl and rendering it outside a form without setUser($viewer).
Common situations: Using generic appendChild() out of habit when adding controls; embedding a tokenizer in a custom (non-AphrontFormView) container; forms whose own viewer was also never set (which surfaces the AphrontFormView render error first or alongside).
Related errors
- You must pass the user to %s.
- You must set a datasource to use a TokenizerControl.
- Call %s when rendering an action as a form.
- Service "%s" is unrecognized, restricted, or you do not have
- When creating a new Almanac interface via the Conduit API, y
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/167d9cbc36af9148.
Report an issue: GitHub.