{"record":{"id":"b23918fd3c1f740f","repo":"phacility/phabricator","slug":"elasticsearch-server-returned-invalid-json","errorCode":null,"errorMessage":"Elasticsearch server returned invalid JSON!","messagePattern":"Elasticsearch server returned invalid JSON!","errorType":"exception","errorClass":"PhutilProxyException","httpStatus":null,"severity":"error","filePath":"src/applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php","lineNumber":536,"sourceCode":"      list($body) = $future->resolvex();\n    } catch (HTTPFutureResponseStatus $ex) {\n      if ($ex->isTimeout() || (int)$ex->getStatusCode() > 499) {\n        $host->didHealthCheck(false);\n      }\n      throw $ex;\n    }\n\n    if ($method != 'GET') {\n      return null;\n    }\n\n    try {\n      $data = phutil_json_decode($body);\n      $host->didHealthCheck(true);\n      return $data;\n    } catch (PhutilJSONParserException $ex) {\n      $host->didHealthCheck(false);\n      throw new PhutilProxyException(\n        pht('Elasticsearch server returned invalid JSON!'),\n        $ex);\n    }\n\n  }\n\n}\n","sourceCodeStart":518,"sourceCodeEnd":544,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/search/fulltextstorage/PhabricatorElasticFulltextStorageEngine.php#L518-L544","documentation":"Thrown by PhabricatorElasticFulltextStorageEngine::executeRequest() when a GET request to a configured Elasticsearch host succeeds at the HTTP level but the response body fails phutil_json_decode(). The host is marked as having failed its health check (didHealthCheck(false)) and the PhutilJSONParserException is rewrapped in a PhutilProxyException. In practice it means the endpoint configured in cluster.search is not speaking the Elasticsearch HTTP API, so every engine operation (indexExists, executeSearch, getIndexStats, indexing writes) fails.","triggerScenarios":"Any bin/search command or object-edit indexing against a cluster.search host whose HTTP response is not JSON: pointing at a port served by a web server or reverse proxy that returns an HTML error/login page, an auth proxy (e.g. basic auth or shield/x-pack) returning a non-JSON body, a firewall/appliance interception page, or a truncated/garbled response.","commonSituations":"cluster.search host/port typo (e.g. pointing at the Phabricator web host instead of ES 9200), ES behind an SSL terminator or SSO proxy, docker port mapping exposing the wrong service, ES not running and something else answering on that port, network middleboxes injecting error pages.","solutions":["From the Phabricator host, run curl -v on the exact configured scheme://host:port/ and inspect the raw body - if it is not JSON you have found the problem","Fix the cluster.search config so each host entry points at the Elasticsearch HTTP endpoint (correct host, port, protocol)","Whitelist the Phabricator host in any proxy/auth layer in front of ES, or remove the layer from that port","Confirm the Elasticsearch major version is one this Phabricator release supports (unsupported versions often serve unexpected payloads)","Re-run bin/search status after each change; the host must report healthy before reindexing"],"exampleFix":"// before (conf/local.json): port 80 is the web server, not Elasticsearch\n\"cluster.search\": [{\"type\": \"elasticsearch\", \"hosts\": [{\"host\": \"127.0.0.1\", \"port\": 80, \"protocol\": \"http\"}]}]\n\n// after: point at the real Elasticsearch HTTP endpoint\n\"cluster.search\": [{\"type\": \"elasticsearch\", \"hosts\": [{\"host\": \"127.0.0.1\", \"port\": 9200, \"protocol\": \"http\"}]}]","handlingStrategy":"try-catch","validationCode":"// Preflight the configured host exactly the way the engine does, before relying on it\n$uri = \"{$protocol}://{$host}:{$port}/\";\n$body = @file_get_contents($uri);\nif ($body === false || json_decode($body) === null) {\n  // host is not speaking JSON: fix cluster.search before indexing\n}","typeGuard":null,"tryCatchPattern":"try {\n  $results = $engine->executeSearch($query);\n} catch (PhutilProxyException $ex) {\n  if ($ex->getPrevious() instanceof PhutilJSONParserException) {\n    // Host answered non-JSON: mark backend down, alert, do not retry blindly\n  }\n  throw $ex;\n}","preventionTips":["Point cluster.search only at a verified Elasticsearch HTTP endpoint; curl it from the Phabricator host first","Run bin/search status after every cluster.search edit","Keep proxies, auth layers, and SSO off the ES port or whitelist the Phabricator host","Pin an Elasticsearch major version your Phabricator release supports"],"tags":["elasticsearch","fulltext","search","json","cluster-config","health-check"],"backgroundTag":"invalid-json-response","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}