fix: retry dashboard terms agg with .keyword on text-field errors (#861)
* fix: retry dashboard terms agg with .keyword on text-field errors Dashboard panels of type `pie` / `bar_h` build a terms aggregation on the field name declared in the panel template (e.g. `agent_name`, `data_win_system_eventID`). Elasticsearch refuses terms aggs on `text`-typed fields by default — they lack per-document field data. Customer indices that haven't received the proper Wazuh template (or were created with a Graylog index set whose custom field mappings don't include `.keyword` subfields) hit this: RequestError(400, 'search_phase_execution_exception', 'Text fields are not optimised for operations that require per-document field data like aggregations and sorting... Please use a keyword field instead.') Other indices in the same deployment can have proper keyword mappings and work fine, which is what makes this surprising in the wild — a dashboard breaks for one customer's Wazuh index but not another's. Fix: catch the specific RequestError, append `.keyword` to the field, and retry once. Cheap one-shot fallback. If the second attempt also fails the original error propagates to the existing per-panel error handler so the panel still renders an error card without taking down the whole dashboard. Helper: _is_text_field_agg_error(exc) checks both `exc.error` (the type code, which is `search_phase_execution_exception`) and `exc.info` (the human-readable message, where "Text fields are not optimised" appears). Using both fields means we don't false-trigger on other search_phase_execution_exceptions like "shard 0 failed: index_not_found_exception". Matches behavior is stable across Elasticsearch 7.x. Note: `str(exc)` on elasticsearch7's RequestError only includes the error code, NOT the message body — the human-readable cause lives on `exc.info`. Easy to get wrong; the helper docstring calls it out. Verified with three constructed RequestError cases in the rebuilt container: 1. real text-field agg error -> match=True 2. unrelated parsing_exception (400) -> match=False 3. search_phase_execution but different -> match=False Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: simplify error handling logic for text field aggregation --------- Co-authored-by: taylor_socfortress <taylor.walton@socfortress.co> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>