phacility/phabricator · error · Exception
Unknown search engine class "%s".
Error message
Unknown search engine class "%s".
What it means
Bulk export jobs serialize the search engine class name in the PhabricatorWorkerBulkJob parameters; when a task runs, PhabricatorExportEngineBulkJobType::runTask() re-instantiates it. The guard is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine') fails when the stored class no longer exists, was renamed, or never was a search engine, so the job cannot proceed and the worker task errors out. This protects against arbitrary class instantiation from job payloads (a mass-assignment/RCE-shaped risk) as well as plain drift.
Source
Thrown at src/infrastructure/export/engine/PhabricatorExportEngineBulkJobType.php:56
$actions[] = id(new PhabricatorActionView())
->setHref($file->getDownloadURI())
->setIcon('fa-download')
->setName(pht('Download Data Export'));
}
}
return $actions;
}
public function runTask(
PhabricatorUser $actor,
PhabricatorWorkerBulkJob $job,
PhabricatorWorkerBulkTask $task) {
$engine_class = $job->getParameter('engineClass');
if (!is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine')) {
throw new Exception(
pht(
'Unknown search engine class "%s".',
$engine_class));
}
$engine = newv($engine_class, array())
->setViewer($actor);
$query_key = $job->getParameter('queryKey');
if ($engine->isBuiltinQuery($query_key)) {
$saved_query = $engine->buildSavedQueryFromBuiltin($query_key);
} else if ($query_key) {
$saved_query = id(new PhabricatorSavedQueryQuery())
->setViewer($actor)
->withQueryKeys(array($query_key))
->executeOne();
} else {
$saved_query = null;View on GitHub (pinned to 5720a38cfe)
Solutions
- Restore the class: re-enable the extension/application that provides the search engine, then let the worker retry the failed tasks (or re-queue them).
- If the class is gone for good, cancel the bulk job in Daemons > Bulk Jobs (or via the job's UI) and re-run the export from a query that still exists.
- For deployments that rename engine classes, drain or complete queued export jobs before the rename ships.
Example fix
// before: extension disabled, queued job references missing engine
$engine_class = $job->getParameter('engineClass'); // 'MyExtThingSearchEngine'
if (!is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine')) {
throw new Exception(...); // worker task fails
}
// after: keep the class loadable (do not disable the app mid-queue), or pre-check when enqueueing
if (!is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine')) {
throw new Exception(pht('...')); // validate BEFORE queuing the bulk job
} Defensive patterns
Strategy: validation
Validate before calling
$engine_class = $job->getParameter('engineClass');
if (!is_subclass_of($engine_class, 'PhabricatorApplicationSearchEngine')) {
throw new Exception(pht('Search engine class no longer available: %s', $engine_class));
} Type guard
function isValidSearchEngineClass($class) {
return is_string($class)
&& class_exists($class)
&& is_subclass_of($class, 'PhabricatorApplicationSearchEngine');
} Try / catch
try {
$this->runTask($actor, $job, $task);
} catch (Exception $ex) {
// permanent failure: do not retry; mark task failed and surface in Bulk Job UI
throw new PhabricatorWorkerPermanentFailureException($ex->getMessage());
} Prevention
- Validate engineClass with is_subclass_of() before enqueueing the bulk job, not only in runTask().
- Drain or cancel queued export jobs before disabling an application or renaming engine classes.
- When cloning prod databases to staging, also deploy the extensions those jobs reference.
When it happens
Trigger: A user queues 'Export to CSV/Excel' from a query powered by a custom/extension search engine, then the extension is disabled, uninstalled, or renamed before the worker task executes - runTask() gets engineClass 'MyExtensionFancySearchEngine' which is no longer loaded; likewise a hand-crafted/corrupted bulk job row.
Common situations: Disabling or upgrading an application right as exports are queued; deploying a refactor that renames engine classes while old bulk jobs linger in the queue; database copies (staging clones of prod) missing the extension code that prod had.
Related errors
- Failed to load saved query ("%s").
- Unknown export format ("%s").
- Atomizer class '%s' must be a concrete subclass of %s.
- Publisher class '%s' must be a concrete subclass of %s.
- No related users have linked Asana accounts.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/517f1281cd96be51.
Report an issue: GitHub.