{"record":{"id":"12f503591f7a280b","repo":"phacility/phabricator","slug":"you-must-set-a-datasource-to-use-a-tokenizercontro","errorCode":null,"errorMessage":"You must set a datasource to use a TokenizerControl.","messagePattern":"You must set a datasource to use a TokenizerControl\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/view/form/control/AphrontFormTokenizerControl.php","lineNumber":65,"sourceCode":"    // render them.\n    $this->loadHandles();\n  }\n\n  protected function renderInput() {\n    $name = $this->getName();\n\n    $handles = $this->loadHandles();\n    $handles = iterator_to_array($handles);\n\n    if ($this->getID()) {\n      $id = $this->getID();\n    } else {\n      $id = celerity_generate_unique_node_id();\n    }\n\n    $datasource = $this->datasource;\n    if (!$datasource) {\n      throw new Exception(\n        pht('You must set a datasource to use a TokenizerControl.'));\n    }\n    $datasource->setViewer($this->getUser());\n\n    $placeholder = $this->placeholder;\n    if ($placeholder === null || !strlen($placeholder)) {\n      $placeholder = $datasource->getPlaceholderText();\n    }\n\n    $values = nonempty($this->getValue(), array());\n    $tokens = $datasource->renderTokens($values);\n\n    foreach ($tokens as $token) {\n      $token->setInputName($this->getName());\n    }\n\n    $template = id(new AphrontTokenizerTemplateView())\n      ->setName($name)","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/view/form/control/AphrontFormTokenizerControl.php#L47-L83","documentation":"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.","triggerScenarios":"Creating an AphrontFormTokenizerControl and setting name/label/value but never calling setDatasource(new SomeDatasource()) before the form renders.","commonSituations":"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.","solutions":["Call setDatasource() with a concrete PhabricatorTypeaheadDatasource, e.g. ->setDatasource(new PhabricatorPeopleDatasource()) or new PhabricatorProjectDatasource().","Check any conditional branches that build the control and ensure every path sets the datasource.","If you only need a fixed option list, use AphrontFormSelectControl instead of a tokenizer."],"exampleFix":"// before\n$control = id(new AphrontFormTokenizerControl())\n  ->setLabel(pht('Reviewers'))\n  ->setName('reviewers')\n  ->setValue($phids);\n// no datasource: renderInput() throws\n\n// after\n$control = id(new AphrontFormTokenizerControl())\n  ->setLabel(pht('Reviewers'))\n  ->setName('reviewers')\n  ->setValue($phids)\n  ->setDatasource(new PhabricatorPeopleDatasource());","handlingStrategy":"validation","validationCode":"// There is no public getDatasource(); guarantee it at construction time:\nfunction newTokenizerControl($name, PhabricatorTypeaheadDatasource $datasource) {\n  return id(new AphrontFormTokenizerControl())\n    ->setName($name)\n    ->setDatasource($datasource);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat setDatasource() as a mandatory constructor argument: build tokenizers only through a helper that typehints the datasource.","A tokenizer without a datasource can never render.","Use AphrontFormSelectControl for fixed option lists instead."],"tags":["php","phabricator","tokenizer","typeahead","form"],"backgroundTag":"missing-datasource","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}