phacility/phabricator · error · Exception

You must set a datasource to use a TokenizerControl.

Error message

You must set a datasource to use a TokenizerControl.

What it means

An AphrontFormTokenizerControl renders a typeahead tokenizer whose suggestion source, token rendering, and placeholder text all come from a PhabricatorTypeaheadDatasource. renderInput() throws when no datasource was set via setDatasource(), because there is no default source of suggestions to fall back to.

Source

Thrown at src/view/form/control/AphrontFormTokenizerControl.php:65

    // render them.
    $this->loadHandles();
  }

  protected function renderInput() {
    $name = $this->getName();

    $handles = $this->loadHandles();
    $handles = iterator_to_array($handles);

    if ($this->getID()) {
      $id = $this->getID();
    } else {
      $id = celerity_generate_unique_node_id();
    }

    $datasource = $this->datasource;
    if (!$datasource) {
      throw new Exception(
        pht('You must set a datasource to use a TokenizerControl.'));
    }
    $datasource->setViewer($this->getUser());

    $placeholder = $this->placeholder;
    if ($placeholder === null || !strlen($placeholder)) {
      $placeholder = $datasource->getPlaceholderText();
    }

    $values = nonempty($this->getValue(), array());
    $tokens = $datasource->renderTokens($values);

    foreach ($tokens as $token) {
      $token->setInputName($this->getName());
    }

    $template = id(new AphrontTokenizerTemplateView())
      ->setName($name)

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Call setDatasource() with a concrete PhabricatorTypeaheadDatasource, e.g. ->setDatasource(new PhabricatorPeopleDatasource()) or new PhabricatorProjectDatasource().
  2. Check any conditional branches that build the control and ensure every path sets the datasource.
  3. If you only need a fixed option list, use AphrontFormSelectControl instead of a tokenizer.

Example fix

// before
$control = id(new AphrontFormTokenizerControl())
  ->setLabel(pht('Reviewers'))
  ->setName('reviewers')
  ->setValue($phids);
// no datasource: renderInput() throws

// after
$control = id(new AphrontFormTokenizerControl())
  ->setLabel(pht('Reviewers'))
  ->setName('reviewers')
  ->setValue($phids)
  ->setDatasource(new PhabricatorPeopleDatasource());
Defensive patterns

Strategy: validation

Validate before calling

// There is no public getDatasource(); guarantee it at construction time:
function newTokenizerControl($name, PhabricatorTypeaheadDatasource $datasource) {
  return id(new AphrontFormTokenizerControl())
    ->setName($name)
    ->setDatasource($datasource);
}

Prevention

When it happens

Trigger: Creating an AphrontFormTokenizerControl and setting name/label/value but never calling setDatasource(new SomeDatasource()) before the form renders.

Common situations: Copy-pasting a tokenizer from another form and losing the datasource line; conditionally setting the datasource in a branch that is skipped; quickly prototyping a custom form.

Related errors


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