phacility/phabricator · error · Exception

Search engine configuration has an invalid service specifica

Error message

Search engine configuration has an invalid service specification (at index "%s"): %s.

What it means

Each service dictionary in `cluster.search` is type-checked with PhutilTypeSpec::checkMap against the allowed schema: `type` (required string) plus optional `hosts` (list of maps), `roles` (map), `port` (int), `protocol` (string), `path` (string), `version` (int). Any unknown key, missing `type`, or wrong value type is caught and rethrown with the index and the underlying check failure appended as the `%s` detail.

Source

Thrown at src/infrastructure/cluster/config/PhabricatorClusterSearchConfigType.php:40

            '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',
          ));
      } catch (Exception $ex) {
        throw new Exception(
          pht(
            'Search engine configuration has an invalid service '.
            'specification (at index "%s"): %s.',
            $index,
            $ex->getMessage()));
      }

      if (!array_key_exists($spec['type'], $engines)) {
        throw new Exception(
          pht(
            'Invalid search engine type: %s. Valid types are: %s.',
            $spec['type'],
            implode(', ', array_keys($engines))));
      }

      if (isset($spec['hosts'])) {
        foreach ($spec['hosts'] as $hostindex => $host) {
          try {

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Read the trailing detail in the message — PhutilTypeSpec names the exact offending key and expected type.
  2. Restrict each service map to type/hosts/roles/port/protocol/path/version with matching types (strings for protocol/path, ints for port/version).
  3. Move per-server connection details into a `hosts` entry (host/roles/port/protocol/path/version), not to the service level.

Example fix

// before
array(
  'type' => 'elasticsearch',
  'host' => 'es1.example.com',
  'port' => '9200',
),
// after
array(
  'type' => 'elasticsearch',
  'hosts' => array(
    array('host' => 'es1.example.com', 'port' => 9200),
  ),
),
Defensive patterns

Strategy: validation

Validate before calling

$service_schema = 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',
);
PhutilTypeSpec::checkMap($spec, $service_schema); // throws with the offending key

Type guard

function isSearchServiceSpec($spec) {
  return is_array($spec)
    && isset($spec['type']) && is_string($spec['type'])
    && (!isset($spec['port']) || is_int($spec['port']))
    && (!isset($spec['version']) || is_int($spec['version']));
}

Prevention

When it happens

Trigger: Writing `'host' => ...` instead of `'hosts'`; adding an unsupported key such as `'timeout'` or `'engine'`; setting `'port' => '9200'` (string) instead of `9200` (int); omitting the required `'type'` key; giving `hosts` a map instead of a list.

Common situations: Transcribing an Elasticsearch URL into config by hand and splitting fields differently than the schema expects; carrying keys over from a different Phabricator version; following outdated documentation.

Related errors


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