What's Actually Happening
Elasticsearch returns index not found errors when queries reference indices that don't exist. This happens due to typos, deleted indices, wrong cluster, or dynamic index naming issues.
The Error You'll See
Query error:
```bash $ curl -X GET "localhost:9200/my_index/_search"
{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index [my_index]", "index": "my_index", "resource.id": "my_index", "resource.type": "index_or_alias" } ], "type": "index_not_found_exception", "reason": "no such index [my_index]" }, "status": 404 } ```
Application error:
ElasticsearchStatusException: Elasticsearch exception [type=index_not_found_exception, reason=no such index [my_index]]
Status: 404
Index: my_indexBulk operation error:
{
"took": 1,
"errors": true,
"items": [
{
"index": {
"_index": "missing_index",
"status": 404,
"error": {
"type": "index_not_found_exception",
"reason": "no such index [missing_index]"
}
}
}
]
}Why This Happens
- 1.Index never created - Index doesn't exist
- 2.Index deleted - Index was removed
- 3.Typos in index name - Wrong index name in query
- 4.Wrong cluster - Querying wrong Elasticsearch node
- 5.Dynamic index pattern - Date-based index doesn't exist for date
- 6.Alias issues - Alias pointing to wrong index
Step 1: List All Indices
```bash # List all indices curl -X GET "localhost:9200/_cat/indices?v"
# Output: health status index pri rep docs.count docs.deleted store.size pri.store.size green open products 5 1 10000 0 50mb 25mb green open users 5 1 5000 0 20mb 10mb green open logs-2026-04 5 1 1000000 0 500mb 250mb
# List indices with specific pattern curl -X GET "localhost:9200/_cat/indices?v&h=index"
# Search for specific index pattern curl -X GET "localhost:9200/_cat/indices?v&s=index" | grep my_index
# Get index settings curl -X GET "localhost:9200/my_index/_settings?pretty"
# Check if index exists (returns 200 or 404) curl -I -X HEAD "localhost:9200/my_index" ```
Step 2: Create Missing Index
```bash # Create index explicitly curl -X PUT "localhost:9200/my_index" -H 'Content-Type: application/json' -d' { "settings": { "number_of_shards": 3, "number_of_replicas": 1 }, "mappings": { "properties": { "title": { "type": "text" }, "date": { "type": "date" }, "tags": { "type": "keyword" } } } } '
# Create index with default settings curl -X PUT "localhost:9200/my_index"
# Verify index created curl -X GET "localhost:9200/_cat/indices?v" | grep my_index
# Check index health curl -X GET "localhost:9200/_cat/indices/my_index?v&h=health,status" ```
Step 3: Create Index Automatically on Document Insert
```bash # Index document - creates index if doesn't exist curl -X POST "localhost:9200/my_index/_doc/1" -H 'Content-Type: application/json' -d' { "title": "My Document", "content": "Some content here" } '
# Note: Auto-creation requires action.auto_create_index enabled # Check setting curl -X GET "localhost:9200/_cluster/settings?include_defaults=true&flat_settings=true" | \ grep auto_create_index
# Enable auto-create curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d' { "persistent": { "action.auto_create_index": true } } '
# Or restrict to patterns curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d' { "persistent": { "action.auto_create_index": "logs*,products*,users*" } } ' ```
Step 4: Fix Index Name Typos
```bash # Common index naming issues
# Case sensitivity - Elasticsearch indices are lowercase only # WRONG: curl -X GET "localhost:9200/MyIndex/_search" # Error: index_not_found_exception
# CORRECT: curl -X GET "localhost:9200/myindex/_search"
# Invalid characters # WRONG: Contains invalid characters (-, +, \, /, *, ?, ", <, >, |, space, comma, #) curl -X PUT "localhost:9200/my index" # Space not allowed # Error: Invalid index name
# CORRECT: curl -X PUT "localhost:9200/my_index"
# Cannot start with -, _, + # WRONG: curl -X PUT "localhost:9200/-myindex" # Error: must not start with '-'
# CORRECT: curl -X PUT "localhost:9200/myindex" ```
Step 5: Handle Dynamic Date-Based Indices
```bash # Logs often use date-based indices: logs-2026-04-15 # If querying logs-2026-04-16 and it doesn't exist yet:
# Option 1: Query with index pattern (matches multiple indices) curl -X GET "localhost:9200/logs-*/_search" -H 'Content-Type: application/json' -d' { "query": { "range": { "@timestamp": { "gte": "2026-04-16", "lte": "2026-04-16" } } } } '
# Option 2: Use wildcard in application # Pattern: logs-{date} # Query: logs-* if specific date index may not exist
# Option 3: Create template for date indices curl -X PUT "localhost:9200/_index_template/logs_template" -H 'Content-Type: application/json' -d' { "index_patterns": ["logs-*"], "template": { "settings": { "number_of_shards": 1, "number_of_replicas": 1 }, "mappings": { "properties": { "@timestamp": { "type": "date" }, "message": { "type": "text" }, "level": { "type": "keyword" } } } } } '
# Now logs-{any_date} auto-creates with this template ```
Step 6: Check Index Aliases
```bash # List all aliases curl -X GET "localhost:9200/_cat/aliases?v"
# Get aliases for specific index curl -X GET "localhost:9200/my_index/_alias/*"
# Check if alias exists curl -I -X HEAD "localhost:9200/_alias/my_alias"
# Alias may point to missing index curl -X GET "localhost:9200/_alias/my_alias?pretty"
# Fix alias pointing to wrong index curl -X POST "localhost:9200/_aliases" -H 'Content-Type: application/json' -d' { "actions": [ { "remove": { "index": "missing_index", "alias": "my_alias" } }, { "add": { "index": "existing_index", "alias": "my_alias" } } ] } '
# Create alias for index curl -X PUT "localhost:9200/my_index/_alias/my_alias"
# Query using alias curl -X GET "localhost:9200/my_alias/_search" ```
Step 7: Verify Cluster Connection
```bash # Check cluster health curl -X GET "localhost:9200/_cluster/health?pretty"
# Check which node you're connected to curl -X GET "localhost:9200/_nodes/local?pretty"
# List all nodes curl -X GET "localhost:9200/_cat/nodes?v"
# Check cluster name curl -X GET "localhost:9200/_cluster/state?pretty" | grep cluster_name
# If querying wrong cluster: # Verify cluster name matches expected
# Check node settings curl -X GET "localhost:9200/_nodes/settings?pretty"
# Test connectivity curl -X GET "localhost:9200/" # Should return cluster info ```
Step 8: Use ignore_unavailable Option
```bash # Query multiple indices, some may not exist # Use ignore_unavailable to skip missing indices
curl -X GET "localhost:9200/index1,index2,missing_index/_search?ignore_unavailable=true" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} } } '
# In application code (Java) SearchRequest request = new SearchRequest("index1", "index2", "missing_index"); request.indicesOptions(IndicesOptions.lenientExpandOpen());
# In Python elasticsearch-py es.search( index=["index1", "index2", "missing_index"], ignore_unavailable=True, body={"query": {"match_all": {}}} )
# Allow no indices option curl -X GET "localhost:9200/nonexistent/_search?allow_no_indices=true&ignore_unavailable=true" ```
Step 9: Restore Deleted Index
```bash # If index was deleted accidentally
# Check if snapshot exists curl -X GET "localhost:9200/_snapshot/_all?pretty"
# List snapshots in repository curl -X GET "localhost:9200/_snapshot/my_repo/_all?pretty"
# Restore from snapshot curl -X POST "localhost:9200/_snapshot/my_repo/snapshot_1/_restore" -H 'Content-Type: application/json' -d' { "indices": "my_index", "include_global_state": false } '
# Verify restore curl -X GET "localhost:9200/_cat/indices?v" | grep my_index
# If no backup, recreate index and reindex from source curl -X PUT "localhost:9200/my_index" curl -X POST "localhost:9200/_reindex" -H 'Content-Type: application/json' -d' { "source": {"index": "source_index"}, "dest": {"index": "my_index"} } ' ```
Step 10: Monitor Index Creation
```bash # Set up alerting for missing indices
# Elasticsearch Watcher (if available) curl -X PUT "localhost:9200/_watcher/watch/index_missing_alert" -H 'Content-Type: application/json' -d' { "trigger": { "schedule": { "interval": "5m" } }, "input": { "search": { "request": { "indices": ["expected_index"], "body": { "query": {"match_all": {}} } } } }, "condition": { "script": "return false" }, "actions": { "email_admin": { "email": { "to": "admin@company.com", "subject": "Index missing", "body": "Expected index not found" } } } } '
# External monitoring script cat << 'EOF' > /usr/local/bin/check_es_indices.sh #!/bin/bash EXPECTED_INDEX="my_index" ES_HOST="localhost:9200"
if ! curl -s -f -X HEAD "$ES_HOST/$EXPECTED_INDEX" > /dev/null; then echo "ALERT: Index $EXPECTED_INDEX not found" mail -s "Elasticsearch Index Missing" admin@company.com <<< \ "Index $EXPECTED_INDEX does not exist" fi EOF
chmod +x /usr/local/bin/check_es_indices.sh ```
Index Naming Rules
| Rule | Example | Valid |
|---|---|---|
| Lowercase only | my_index | Yes |
| No uppercase | MyIndex | No |
| No spaces | my_index | Yes |
| No leading -/_/+ | -myindex | No |
| No invalid chars | my#index | No |
| Max 255 bytes | long_name... | Limited |
Verify the Fix
```bash # After creating or fixing index reference
# 1. Verify index exists curl -I -X HEAD "localhost:9200/my_index" # Should return 200 OK
# 2. List indices curl -X GET "localhost:9200/_cat/indices?v" | grep my_index
# 3. Test query curl -X GET "localhost:9200/my_index/_search?size=1" # Should return results or empty hits, not 404
# 4. Check index health curl -X GET "localhost:9200/_cat/indices/my_index?v&h=health,status,docs.count"
# 5. Verify cluster curl -X GET "localhost:9200/_cluster/health?pretty" # Status should be green or yellow
# 6. Check aliases if used curl -X GET "localhost:9200/_cat/aliases?v" | grep my_alias
# 7. Test application query # Application should successfully query index ```
Related Issues
- [Fix Elasticsearch Cluster Unhealthy](/articles/fix-elasticsearch-cluster-unhealthy)
- [Fix Elasticsearch Query Parse Error](/articles/fix-elasticsearch-query-parse-error)
- [Fix Elasticsearch Mapping Conflict](/articles/fix-elasticsearch-mapping-conflict)