phacility/phabricator · error · Exception
Search cluster configuration is not valid: each entry in the
Error message
Search cluster configuration is not valid: each entry in the list must be a dictionary describing a search service, but the value with index "%s" is not a dictionary.
What it means
`cluster.search` must be a list where every entry is a dictionary describing one search service. PhabricatorClusterSearchConfigType::validateValue iterates the list and throws when an entry is a scalar or a list — e.g. a bare engine name — instead of a map. Validation runs through validateStoredValue whenever the option is saved.
Source
Thrown at src/infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php:19
<?php
final class PhabricatorClusterSearchConfigType
extends PhabricatorJSONConfigType {
const TYPEKEY = 'cluster.search';
public function validateStoredValue(
PhabricatorConfigOption $option,
$value) {
self::validateValue($value);
}
public static function validateValue($value) {
$engines = PhabricatorSearchService::loadAllFulltextStorageEngines();
foreach ($value as $index => $spec) {
if (!is_array($spec)) {
throw new Exception(
pht(
'Search cluster configuration is not valid: each entry in the '.
'list must be a dictionary describing a search service, but '.
'the value with index "%s" is not a dictionary.',
$index));
}
try {
PhutilTypeSpec::checkMap(
$spec,
array(
'type' => 'string',
'hosts' => 'optional list<map<string, wild>>',
'roles' => 'optional map<string, wild>',
'port' => 'optional int',
'protocol' => 'optional string',
'path' => 'optional string',
'version' => 'optional int',View on GitHub (pinned to 5720a38cfe)
Solutions
- Wrap each service as a dictionary whose required first key is `type`, e.g. `array('type' => 'elasticsearch', 'hosts' => array(...))`.
- Remove any scalar or plain-list entries from `cluster.search`.
- Prefer the Config → Search Servers UI, which produces the correct structure.
Example fix
// before
'cluster.search' => array('elasticsearch'),
// after
'cluster.search' => array(
array(
'type' => 'elasticsearch',
'hosts' => array(
array('host' => 'es1.example.com', 'port' => 9200),
),
),
), Defensive patterns
Strategy: validation
Validate before calling
// Before saving cluster.search, mirror the config-type check:
function assertSearchEntriesAreMaps(array $cluster_search) {
foreach ($cluster_search as $index => $spec) {
if (!is_array($spec) || (array_keys($spec) === range(0, count($spec) - 1))) {
throw new Exception(
'cluster.search['.$index.'] must be a dictionary, e.g. '.
"array('type' => 'mysql')");
}
}
} Type guard
function isSearchServiceSpec($spec) {
return is_array($spec) && isset($spec['type']) && is_string($spec['type']);
} Prevention
- Prefer the Config → Search Servers UI over hand-writing the raw value; it emits the correct structure.
- Unit-test your config generation by running PhabricatorClusterSearchConfigType::validateValue() on the produced value in CI.
- Start from a known-good example (`array(array('type' => 'mysql'))`) and add fields incrementally, saving after each change so validation catches drift early.
When it happens
Trigger: Setting `cluster.search` to `array('elasticsearch')` (string entry), `array('mysql', 'elasticsearch')` (scalars), or a doubly-nested list, instead of `array(array('type' => 'mysql'))`. Triggered when saving the option via the config application or `bin/config set`.
Common situations: Hand-writing the config from memory instead of docs; converting config from YAML/JSON and losing a nesting level; older examples where a simpler format was shown.
Related errors
- Search engine configuration has an invalid service specifica
- Invalid search engine type: %s. Valid types are: %s.
- Search cluster configuration has an invalid host specificati
- The following regex is malformed and cannot be used: %s
- Configured search engine type "%s" is unknown. Valid engines
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/17290cf3ced478be.
Report an issue: GitHub.