How to fix ParsingException: Unknown key for a START_OBJECT in [query] in Elasticsearch

ElasticsearchINTERMEDIATEMEDIUMUpdated Dec 22, 2025· 184 views

This error occurs when Elasticsearch encounters an unexpected or invalid key while parsing a query JSON object. The query structure doesn't conform to the Elasticsearch Query DSL syntax, typically due to incorrect nesting, misplaced query clauses, or malformed JSON.

Quick fix

Fixes ParsingException: Unknown key for a START_OBJECT in [query]

# The error message typically looks like:
# "parsing_exception: Unknown key for a START_OBJECT in [filter]"
# "reason": "Unknown key for a START_OBJECT in [filter]",
# "line": 3,
# "col": 5

# Run the query with verbose error output
curl -X GET "localhost:9200/my-index/_search?error_trace=true" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  },
  "filter": {
    "term": { "status": "active" }
  }
}
'
  1. Identify the exact location of the parsing error
  2. Fix incorrect filter placement
  3. Fix incorrect bool query structure
  4. Validate JSON structure and syntax
elasticsearch — error
# The error message typically looks like:
# "parsing_exception: Unknown key for a START_OBJECT in [filter]"
# "reason": "Unknown key for a START_OBJECT in [filter]",
# "line": 3,
# "col": 5
# Run the query with verbose error output
curl -X GET "localhost:9200/my-index/_search?error_trace=true" -u "username:password" -H 'Content-Type: application/json' -d'
{
"query": {
"match_all": {}
},
"filter": {
"term": { "status": "active" }
}
}
'
ParsingException: Unknown key for a START_OBJECT in [query]

What this error means

The "ParsingException: Unknown key for a START_OBJECT" error indicates that Elasticsearch's query parser encountered a JSON object with a key it doesn't recognize at that position in the query structure. This is a parsing error that occurs during query validation, before the query is executed. Elasticsearch uses a strict JSON parser that expects queries to follow the Query DSL (Domain Specific Language) syntax. When it starts parsing an object (indicated by START_OBJECT, which is a '{' character), it expects specific keys based on the context. If it encounters a key that isn't valid in that position, it throws this parsing exception. Common scenarios that trigger this error: 1. Placing query clauses like "filter", "must", or "should" at the wrong nesting level 2. Using numeric or invalid keys where field names are expected 3. Incorrectly structuring bool queries or other compound queries 4. Query DSL syntax changes between Elasticsearch versions (especially 5.x and later) 5. Missing required wrapper objects around query clauses The error message typically includes the specific key that caused the problem, which helps identify where in your query the structure is incorrect.

Typical symptoms

  • Query requests fail immediately with "ParsingException" errors
  • Error message indicates line and column numbers pointing to the malformed JSON
  • Queries that worked in older Elasticsearch versions fail after upgrade
  • REST API returns 400 Bad Request status with parsing error details
  • Kibana Dev Tools highlights syntax errors in the query editor
  • Application logs show repeated query parsing failures
  • Bulk query operations fail with parsing errors for specific queries

Common causes

  1. Filter clause placed at root level instead of inside a bool query
  2. Using numeric keys (like "0", "1", "2") where field names are expected
  3. Incorrect nesting of query clauses (must, should, filter, must_not)
  4. Missing "query" wrapper object around search clauses
  5. Version incompatibility - query syntax changed between Elasticsearch 2.x and 5.x
  6. Malformed JSON with extra commas, missing brackets, or unbalanced braces
  7. Using deprecated query syntax that is no longer valid
  8. Incorrectly placed aggregation or runtime_mappings objects

How to fix "ParsingException: Unknown key for a START_OBJECT in [query]"

1Identify the exact location of the parsing error

Examine the full error message to find the problematic key and location:

bash
# The error message typically looks like:
# "parsing_exception: Unknown key for a START_OBJECT in [filter]"
# "reason": "Unknown key for a START_OBJECT in [filter]",
# "line": 3,
# "col": 5

# Run the query with verbose error output
curl -X GET "localhost:9200/my-index/_search?error_trace=true" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  },
  "filter": {
    "term": { "status": "active" }
  }
}
'

The error will point to the specific key (in this example, "filter") that is in the wrong position. Note the line and column numbers to locate the issue in your query.

2Fix incorrect filter placement

The most common cause is placing "filter" at the root level. In Elasticsearch 5.x+, filters must be inside a bool query:

bash
# INCORRECT - filter at root level
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "match_all": {}
  },
  "filter": {
    "term": { "status": "active" }
  }
}
'

# CORRECT - filter inside bool query
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match_all": {} }
      ],
      "filter": [
        { "term": { "status": "active" } }
      ]
    }
  }
}
'

# Alternative: Use bool query with only filter (more efficient)
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } },
        { "range": { "created_at": { "gte": "2024-01-01" } } }
      ]
    }
  }
}
'
3Fix incorrect bool query structure

Ensure must, should, filter, and must_not clauses are properly structured:

bash
# INCORRECT - should clause not in array
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "should": {
        "match": { "title": "error" }
      }
    }
  }
}
'

# CORRECT - should clause in array
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "should": [
        { "match": { "title": "error" } },
        { "match": { "description": "exception" } }
      ],
      "minimum_should_match": 1
    }
  }
}
'

# CORRECT - combining multiple bool clauses
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "status": "published" } }
      ],
      "should": [
        { "match": { "title": "elasticsearch" } },
        { "match": { "tags": "database" } }
      ],
      "filter": [
        { "range": { "created_at": { "gte": "2024-01-01" } } }
      ],
      "must_not": [
        { "term": { "archived": true } }
      ],
      "minimum_should_match": 1
    }
  }
}
'
4Validate JSON structure and syntax

Use JSON validators and Kibana Dev Tools to catch structural errors:

bash
# Use jq to validate JSON syntax
echo '{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } }
      ]
    }
  }
}' | jq .

# Use Python to validate and format
python3 << 'EOF'
import json

query = '''
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } }
      ]
    }
  }
}
'''

try:
    parsed = json.loads(query)
    print("Valid JSON:")
    print(json.dumps(parsed, indent=2))
except json.JSONDecodeError as e:
    print(f"JSON Error: {e}")
EOF

Using Kibana Dev Tools:
1. Open Kibana Dev Tools (Console)
2. Paste your query
3. Kibana will highlight syntax errors in red
4. Hover over errors for suggestions
5. Use auto-complete (Ctrl+Space) for valid query structures

Common JSON errors:
- Extra commas: { "field": "value", }
- Missing commas: { "a": 1 "b": 2 }
- Unbalanced brackets: { "query": { "match": {} }
- Invalid key names: numeric keys where field names expected

5Fix numeric or invalid key names

Replace numeric keys with proper field names or restructure the query:

bash
# INCORRECT - using numeric keys in fields object
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "_source": {
    "0": "field1",
    "1": "field2"
  }
}
'

# CORRECT - use array for fields
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": { "match_all": {} },
  "_source": ["field1", "field2"]
}
'

# INCORRECT - numeric keys in aggregations
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "aggs": {
    "0": {
      "terms": { "field": "category" }
    }
  }
}
'

# CORRECT - use descriptive aggregation names
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "aggs": {
    "categories": {
      "terms": {
        "field": "category.keyword",
        "size": 10
      }
    }
  }
}
'
6Update queries for version compatibility

If migrating from Elasticsearch 2.x to 5.x+, update deprecated syntax:

bash
# Elasticsearch 2.x syntax (deprecated)
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "filtered": {
      "query": { "match_all": {} },
      "filter": { "term": { "status": "active" } }
    }
  }
}
'

# Elasticsearch 5.x+ syntax (current)
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": { "match_all": {} },
      "filter": { "term": { "status": "active" } }
    }
  }
}
'

# Update deprecated query types
# Old: "and", "or", "not" filters
# New: bool query with "must", "should", "must_not"

# Old syntax (Elasticsearch 2.x)
{
  "query": {
    "filtered": {
      "filter": {
        "and": [
          { "term": { "status": "active" } },
          { "range": { "price": { "gte": 10 } } }
        ]
      }
    }
  }
}

# New syntax (Elasticsearch 5.x+)
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } },
        { "range": { "price": { "gte": 10 } } }
      ]
    }
  }
}

Advanced notes

## Advanced Query Debugging Techniques

### Using Elasticsearch Validate API

The Validate API helps identify query issues before execution:

bash
# Validate query and get explanations
curl -X GET "localhost:9200/my-index/_validate/query?explain=true" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } }
      ]
    }
  }
}
'

# Response shows if query is valid and how it's interpreted
# {
#   "valid": true,
#   "explanations": [
#     {
#       "index": "my-index",
#       "valid": true,
#       "explanation": "status:active"
#     }
#   ]
# }

### Query Rewrite for Understanding

Use the rewrite parameter to see how Elasticsearch processes your query:

bash
curl -X GET "localhost:9200/my-index/_search?rewrite=true" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "filter": [
        { "prefix": { "title": "elastic" } }
      ]
    }
  }
}
'

### Common Query DSL Patterns

Combining Multiple Conditions:

json
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "elasticsearch" } }
      ],
      "should": [
        { "term": { "priority": "high" } },
        { "range": { "score": { "gte": 8 } } }
      ],
      "filter": [
        { "term": { "status": "published" } },
        { "range": { "created_at": { "gte": "now-30d" } } }
      ],
      "must_not": [
        { "term": { "archived": true } }
      ],
      "minimum_should_match": 1,
      "boost": 1.2
    }
  }
}

Nested Queries:

json
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "must": [
            { "match": { "comments.author": "john" } },
            { "range": { "comments.created_at": { "gte": "2024-01-01" } } }
          ]
        }
      }
    }
  }
}

### Performance Considerations

Filter vs Must:
- Use filter for exact matches and ranges (not scored, cacheable)
- Use must for full-text search that needs scoring
- Filters are faster and use less memory due to caching

Query Optimization:

json
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } },
        { "range": { "timestamp": { "gte": "now-1d" } } }
      ],
      "must": [
        { "match": { "description": "error" } }
      ]
    }
  },
  "size": 10,
  "_source": ["id", "title", "timestamp"],
  "track_total_hits": false
}

### Debugging with Explain API

See exactly why a document matches (or doesn't match):

bash
# Explain why a specific document matches
curl -X GET "localhost:9200/my-index/_explain/document-id" -u "username:password" -H 'Content-Type: application/json' -d'
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "elasticsearch" } }
      ],
      "filter": [
        { "term": { "status": "active" } }
      ]
    }
  }
}
'

### Client Library Query Building

Using official clients helps avoid syntax errors:

python
# Python Elasticsearch client
from elasticsearch import Elasticsearch

es = Elasticsearch(["http://localhost:9200"])

# Query builder prevents structure errors
query = {
    "bool": {
        "must": [
            {"match": {"title": "elasticsearch"}}
        ],
        "filter": [
            {"term": {"status": "active"}},
            {"range": {"created_at": {"gte": "2024-01-01"}}}
        ]
    }
}

response = es.search(index="my-index", query=query)
javascript
// JavaScript Elasticsearch client
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });

const response = await client.search({
  index: 'my-index',
  body: {
    query: {
      bool: {
        must: [
          { match: { title: 'elasticsearch' } }
        ],
        filter: [
          { term: { status: 'active' } },
          { range: { created_at: { gte: '2024-01-01' } } }
        ]
      }
    }
  }
});

### Version Migration Checklist

When upgrading Elasticsearch versions:
1. Review breaking changes in official migration guide
2. Test queries in development environment
3. Use Validate API to check all queries
4. Update deprecated query syntax before upgrade
5. Monitor parsing errors in logs after upgrade
6. Implement query version detection in application code

Frequently asked questions

What does "ParsingException: Unknown key for a START_OBJECT in [query]" mean?
The "ParsingException: Unknown key for a START_OBJECT" error indicates that Elasticsearch's query parser encountered a JSON object with a key it doesn't recognize at that position in the query structure. This is a parsing error that occurs during query validation, before the query is executed. Elasticsearch uses a strict JSON parser that expects queries to follow the Query DSL (Domain Specific Language) syntax.
How do I fix "ParsingException: Unknown key for a START_OBJECT in [query]"?
This error occurs when Elasticsearch encounters an unexpected or invalid key while parsing a query JSON object. The query structure doesn't conform to the Elasticsearch Query DSL syntax, typically due to incorrect nesting, misplaced query clauses, or malformed JSON.
What causes "ParsingException: Unknown key for a START_OBJECT in [query]"?
Filter clause placed at root level instead of inside a bool query Using numeric keys (like "0", "1", "2") where field names are expected Incorrect nesting of query clauses (must, should, filter, must_not)
Is the "ParsingException: Unknown key for a START_OBJECT in [query]" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading