Elasticsearch returns HTTP 404 in several distinct situations, and the meaning shifts depending on whether you are requesting an index, a document, an alias, or an API endpoint. Understanding which resource triggered the 404 is the first step in diagnosing what went wrong. The error response body almost always tells you exactly what Elasticsearch could not find, so reading the JSON payload is non-negotiable.
Contexts Where 404 Occurs
A GET /my-index request against a non-existent index returns 404 with an index_not_found_exception in the body. The response includes the index name that was not found, making it straightforward to identify typos or references to indices that have been deleted or rotated.
A GET /my-index/_doc/123 request against an existing index but a missing document also returns 404. In this case, the body contains "found": false rather than an exception type. This is normal behavior - the index exists but the specific document ID does not. Your application code should distinguish between these two cases: one indicates a missing resource, the other a configuration or infrastructure problem.
Alias lookups behave similarly. Requesting GET /_alias/my-alias when the alias does not exist returns 404. Hitting a non-existent REST endpoint, such as GET /_nonexistent, also yields 404 but with a different error structure indicating no handler was found for the URI.
The index_not_found_exception Error
When an index is missing, Elasticsearch returns a structured error:
{
"error": {
"root_cause": [{
"type": "index_not_found_exception",
"reason": "no such index [logs-2025.01.15]",
"resource.type": "index_or_alias",
"resource.id": "logs-2025.01.15",
"index": "logs-2025.01.15"
}],
"type": "index_not_found_exception",
"reason": "no such index [logs-2025.01.15]",
"index": "logs-2025.01.15"
},
"status": 404
}
The resource.id and index fields contain the exact name that failed resolution. This is particularly useful when a request targets multiple indices and you need to identify which one is missing.
Common triggers include typos in index names, time-based indices that have rolled over or been deleted by ILM, and aliases that were removed during a reindex operation. If you are querying logs-2025.01.15 but your ILM policy deleted indices older than 30 days, the index simply no longer exists.
Document Not Found vs Index Not Found
The distinction between a 404 for a missing document and a 404 for a missing index is worth internalizing. Both return HTTP 404 but carry different payloads.
GET /existing-index/_doc/missing-id returns:
{
"_index": "existing-index",
"_id": "missing-id",
"found": false
}
This is a perfectly normal response. The index was resolved, the shard was queried, and the document was not there. No exception, no error object.
GET /nonexistent-index/_doc/123 returns the index_not_found_exception structure described above. Client libraries handle these differently. For example, the Elasticsearch Python client raises a NotFoundError for both, but the response body distinguishes them. Treating all 404s as "document not found" is a common bug in application code.
The allow_no_indices and ignore_unavailable Parameters
Elasticsearch provides two request parameters that modify how missing or closed indices are handled in multi-index requests.
ignore_unavailable, when set to true, silently skips indices that are missing or closed rather than returning 404. This is useful for queries spanning time-based index patterns where some indices may have been deleted. A search across logs-2025.01.* with ignore_unavailable=true will not fail if logs-2025.01.01 has been removed by retention policy.
allow_no_indices controls what happens when a wildcard pattern or alias resolves to zero indices. With allow_no_indices=false, a pattern like logs-2099.* that matches nothing returns a 404. With the default value of true, Elasticsearch returns an empty result set instead. This parameter only affects wildcard resolution - it has no effect on explicit index names.
Both parameters apply to search, count, delete-by-query, and several other multi-index APIs. They do not apply to the single-document APIs like GET /index/_doc/id.
Wildcards and Data Streams
Wildcard patterns interact with 404 logic in specific ways. A request to GET /logs-*/_search succeeds as long as at least one index matches the pattern (assuming allow_no_indices=true). If no index matches and allow_no_indices=false, you get 404.
Data streams add another layer. The backing indices of a data stream have system-generated names like .ds-logs-2025.01.15-000001. Querying a data stream by its name (e.g., logs) works as expected, but querying the backing indices directly requires knowing the full generated name. A request targeting logs succeeds if the data stream exists, even if you expected it to be a regular index.
The expand_wildcards parameter also interacts here. By default, wildcards expand to open indices only. If your data stream's backing indices are in a non-default state, or if you are targeting hidden indices, you may need expand_wildcards=all or expand_wildcards=hidden to avoid unexpected 404s.
When debugging 404 errors in wildcard or data stream scenarios, start by listing the actual indices that exist using GET /_cat/indices/logs-* or GET /_data_stream/logs. This tells you what Elasticsearch can actually resolve, which is faster than guessing at pattern matching behavior.