Troubleshooting Elasticsearch connections and queries
Most problems show up in the connection test or in the note above a result. This page lists what each message means and what to change.
Run the connection test first#
Every connection has a Test button that runs five stages in order: DNS → TLS → Access layer → Authentication → Cluster. Each reports ok, a warning or a failure with a reason and timing; fix the first failing stage.
Authentication: 401 and 403#
Authentication failed (401). The cluster rejected the credentials. Re-enter the API key or the username and password and run Test. Paste an API key exactly as Elasticsearch printed it (the base64 id:api_key form); an invalidated or expired key gives the same error.
401 "not allowed" on Amazon OpenSearch Service. The domain answers 401 for API paths it does not expose, even with valid credentials. FlatQL says so in the hint: the path was rejected, not your credentials. Use the Browse page and SQL rather than the raw endpoint that was refused.
Authenticated but not allowed (403). The credentials are valid but lack a privilege. Querying needs read access to the indices plus their mappings and field capabilities, which FlatQL uses to plan statements; document edits and Load data need write access as well.
403 with a non-JSON body. Something in front of the cluster answered, not Elasticsearch itself: a proxy, a WAF or a zero-trust gateway. The connection test reports it under Access layer. If the cluster sits behind Cloudflare Access, add the service token headers on the connection; a redirect to a cloudflareaccess.com login page is reported as service token missing or not allowed by the policy.
Unreachable host#
Could not connect within 20 s: the host is not answering. Wrong host or port, a firewall, or the cluster is down. Check the URL includes the scheme and port (https://es.example.com:9200). From the web app, the host must be reachable from FlatQL's servers; a cluster on localhost or a private network is a job for the desktop app.
DNS failure. The host name does not resolve. Typo, or an internal name that only resolves inside your network.
After a failure FlatQL treats the cluster as down for 15 seconds and fails other calls to it at once; wait a moment before pressing Test again.
TLS and self-signed certificates#
Certificate verification failed. No trusted authority signed the cluster's certificate. In order of preference:
- Paste the CA certificate (PEM) into the connection's TLS settings. Verification stays on and the private authority is trusted for this connection only.
- For a development cluster with a self-signed certificate, switch Verify certificate off. The test then marks the TLS stage with a warning.
Any other TLS error usually means an http URL for a cluster that speaks https, or the reverse. On success the TLS stage shows the negotiated version, issuer and expiry, which also catches an expired certificate.
A note on CORS#
The web app never calls the cluster from your browser: every request goes to FlatQL's backend, which calls the cluster from its servers, so the cluster's http.cors.* settings are not involved. The desktop app calls the cluster from its own local backend process, also outside the browser.
Timeouts#
Two timeouts apply to every request:
| Setting | Default | Where |
|---|---|---|
| Connect timeout | 20 s | Per connection, 1 to 300 s; empty means the server default |
| Request timeout | 30 s | Per connection safety settings |
Timed out waiting for a response after 30 s. The cluster accepted the connection but did not answer in time, usually because a statement scans a large index (its filter could not be pushed down, see below) or aggregates over many buckets. Raise the request timeout on the connection, or narrow the statement.
Mapping conflicts in index patterns#
An index pattern such as logs-* can span indices that map one field differently: status_code as integer in one month and keyword in another. FlatQL reads field capabilities for the whole pattern and uses the first concrete type it finds, so status_code >= 500 works where the field is numeric and may return nothing from the rest.
Symptoms: fewer rows than expected, or a cast error from the engine. Fixes: narrow the pattern to indices that agree (logs-2026-10-*), use TRY_CAST(status_code AS INTEGER), or fix the template. Type mapping explains how each type is read. The other common surprise is a text field: it cannot be grouped or compared for equality on the cluster, so WHERE name = 'Acme' runs in the engine over a scan; see keyword vs text.
Large result sets and the fetch cap#
The embedded engine pulls rows from the cluster only for what it cannot push down. The engine's rules are:
WHERE: a conjunct is pushed only when it maps 1:1 to a filter (column vs literal on a field we can filter exactly). The full WHERE still runs in DuckDB; pushing just shrinks the fetch. LIMIT / ORDER BY: pushed only for single-table statements with no aggregation, DISTINCT, window or QUALIFY, and only when the whole WHERE was pushed. GROUP BY: pushed as a composite aggregation when every grouping key is a plain groupable field, every aggregate is COUNT / SUM / AVG / MIN / MAX (plus approximate COUNT DISTINCT) on a plain field, and the WHERE was pushed in full. Otherwise the index is scanned up to the fetch cap and the response carries a warning.
The fetch cap is 50,000 rows per index per statement. When it applies you see a note such as Part of the WHERE clause on orders could not be sent to Elasticsearch, so up to 50,000 rows were scanned and filtered here. The result is then computed over a sample and may be incomplete.
-- scanned: the function on the column cannot be pushed
SELECT * FROM orders WHERE LOWER(status) = 'shipped';
-- pushed: a plain comparison on a keyword field
SELECT * FROM orders WHERE status = 'shipped';
Rewrite the predicate as a plain comparison, add a pushable filter (a date range, a keyword equality) that brings the set under the cap, or use Cluster SQL mode when the statement needs no joins. A GROUP BY with more than 50,000 groups is cut at 50,000 with a note. See pushdown for the full list and limitations for what any SQL layer over Elasticsearch can do.
Export failures#
- Export needs the Pro plan. Exports are not part of Free; see pricing.
- Download failed mid-way. The cluster timed out, the tab was closed, or the browser could not write to disk. The partial file stays where you saved it; re-run with a narrower statement or fewer rows.
- Fewer rows than requested. The statement has its own
LIMIT, or the index has fewer matching rows. A toast says which. - Excel stops at 100,000 rows. The
.xlsxfile is built in the browser's memory; pick CSV for Excel, which streams. - Memory pressure in Safari or Firefox. Only Chrome and Edge stream to disk; other browsers hold the file in memory until the end. Details on the export page.
Still stuck? Send the exact error text and the failing test stage through the contact page, without credentials.