Skip to content

fix(search): /api/es/search returns HTTP 500 "JSON does not allow non-finite numbers" on field-sorted and aggregation/filter queries #36478

Description

@dsilvam

Problem Statement

Confirmed platform-wide regression. The /api/es/search and /api/es/raw REST endpoints return HTTP 500 with the error:

JSON does not allow non-finite numbers.

This affects any consumer whose search results carry a non-finite _score — most commonly:

  • queries that sort by a field (Elasticsearch sets _score to NaN unless track_scores=true),
  • filter / constant_score / aggregation-only contexts.

It is not content-specific (reproducible on Production, Staging, and UAT with unchanged content) and hits built-in features such as Site Search as well as custom /api/es/search calls.

Root cause

Introduced by PR #36398 (route /api/es/raw & /api/es/search through phase-aware SearchAPI), first released in v26.07.04-01.

The endpoints were rewritten to rebuild the legacy Elasticsearch-wire response via dotCMS's com.dotmarketing.util.json.JSONObject (ESContentResourcePortlet.toLegacyEsJsonhitsToLegacyJson). The per-hit score is added with a raw float:

// ESContentResourcePortlet.hitsToLegacyJson
.put("_score", hit.getScore())   // hit.getScore() can be Float.NaN

JSONObject.put(...) runs testValidity(), which throws on non-finite values:

// com.dotmarketing.util.json.JSONObject.testValidity
if (((Float) o).isInfinite() || ((Float) o).isNaN())
    throw new JSONException("JSON does not allow non-finite numbers.");

The previous implementation returned Elasticsearch's native JSON (SearchResponse.toString()), which serializes NaN as null — so the error did not occur before the cutover.

Steps to Reproduce

  1. POST /api/es/search with a query that sorts by a field (e.g. a date field) and does not set track_scores: true — or a plain terms search combined with a content-type aggregation.
  2. Observe the response.

Actual: HTTP 500 — JSON does not allow non-finite numbers.
Expected: valid JSON with "_score": null for non-scored hits, matching the pre-regression Elasticsearch-native wire format.

Acceptance Criteria

  • /api/es/search and /api/es/raw return valid JSON (HTTP 200) for field-sorted queries that produce NaN scores.
  • Non-finite (NaN/Infinity) _score values serialize as null, preserving the legacy Elasticsearch-wire shape.
  • The same guard is applied to every raw float/double written into the response JSONObject (per-hit _score and the suggester score fields).
  • No change to the response shape/contract for finite scores; no schema or index changes.
  • A regression test maps a ContentSearchResponse whose hit carries Float.NaN through toLegacyEsJson and asserts valid JSON with "_score": null (instead of throwing).

dotCMS Version

Severity

High - Major functionality broken (search endpoints and Site Search fail in production; not content-related).

Links

Affected file: dotCMS/src/main/java/com/dotcms/rest/elasticsearch/ESContentResourcePortlet.java

Metadata

Metadata

Type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions