This error occurs when Elasticsearch encounters a document with fields not defined in the index mapping, and the mapping is configured with "dynamic": "strict". Elasticsearch rejects the document to prevent schema drift and ensure data consistency. The fix involves updating your mapping to allow dynamic fields or explicitly defining all expected fields.
The "StrictDynamicMappingException: mapping set to strict, dynamic introduction of [field] within [type] is not allowed" error indicates that Elasticsearch is configured to reject documents containing fields that aren't explicitly defined in the index mapping. Elasticsearch mappings define the schema for your documents - what fields exist, their data types, and how they should be indexed. The "dynamic" setting controls how Elasticsearch handles new fields in documents: - **"dynamic": "true"** (default): New fields are automatically added to the mapping - **"dynamic": "false"**: New fields are ignored (not indexed/searchable) - **"dynamic": "strict"**: New fields cause an exception and the document is rejected When set to "strict", Elasticsearch enforces schema validation, preventing unexpected fields from being indexed. This is useful for: - Ensuring data consistency in production systems - Preventing typos or incorrect field names - Maintaining predictable query performance - Avoiding mapping explosions from uncontrolled field creation The error typically occurs when: 1. Your application evolves and starts sending new fields 2. Different data sources send documents with varying structures 3. There's a mismatch between development and production mappings 4. You're indexing documents from external systems with unknown schemas
First, examine your index mapping to confirm it's set to "strict" mode:
# Get the mapping for a specific index
curl -X GET "localhost:9200/my-index/_mapping" -u "username:password"
# Get mapping for all indices
curl -X GET "localhost:9200/_mapping" -u "username:password"
# Check the dynamic setting in the mapping
curl -X GET "localhost:9200/my-index/_mapping/field/dynamic" -u "username:password"
# Example response showing strict mapping:
# {
# "my-index": {
# "mappings": {
# "dynamic": "strict",
# "properties": {
# "title": { "type": "text" },
# "timestamp": { "type": "date" }
# // Only these fields are allowed
# }
# }
# }
# }Look for "dynamic": "strict" in the mapping response. This confirms your index rejects unknown fields.
Examine the failing documents to see which fields are causing the error:
# Check Elasticsearch logs for the exact error
tail -f /var/log/elasticsearch/elasticsearch.log | grep StrictDynamicMappingException
# If using the Elasticsearch REST API, the error response includes the field name:
# {
# "error": {
# "type": "strict_dynamic_mapping_exception",
# "reason": "mapping set to strict, dynamic introduction of [new_field] within [my-type] is not allowed"
# }
# }
# For bulk operations, check individual failures
curl -X POST "localhost:9200/_bulk" -u "username:password" -H 'Content-Type: application/json' -d'
{ "index" : { "_index" : "my-index", "_id" : "1" } }
{ "title": "Test", "new_field": "value", "timestamp": "2024-01-01" }
{ "index" : { "_index" : "my-index", "_id" : "2" } }
{ "title": "Test 2", "timestamp": "2024-01-02" }
'
# The response will show which document failed and whyMake a list of all fields that need to be added to your mapping.
For immediate resolution, you can change the dynamic setting to "true" or "false":
# Update mapping to allow dynamic fields (not recommended for production)
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": true
}
'
# Or set to "false" to ignore unknown fields (they won't be indexed)
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": false
}
'
# For multiple indices
curl -X PUT "localhost:9200/my-index-*/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": true
}
'
# Note: You may need to close and reopen the index for mapping changes
curl -X POST "localhost:9200/my-index/_close" -u "username:password"
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": true
}
'
curl -X POST "localhost:9200/my-index/_open" -u "username:password"Warning: Setting "dynamic": true can lead to mapping explosions if uncontrolled. Use this only as a temporary fix.
The correct solution is to explicitly define all expected fields in your mapping:
# Update mapping with all required fields
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": "strict",
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"timestamp": {
"type": "date"
},
"new_field": {
"type": "keyword" # Add the previously missing field
},
"another_field": {
"type": "integer"
},
"nested_object": {
"type": "nested",
"properties": {
"subfield": { "type": "text" }
}
}
}
}
'
# Use index templates for consistent mappings across indices
curl -X PUT "localhost:9200/_index_template/my-template" -u "username:password" -H 'Content-Type: application/json' -d'
{
"index_patterns": ["logs-*"],
"template": {
"mappings": {
"dynamic": "strict",
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" },
"level": { "type": "keyword" },
"service": { "type": "keyword" }
}
}
},
"priority": 200
}
'Define appropriate field types (text, keyword, date, integer, etc.) based on how you need to search and aggregate the data.
Add validation to ensure documents match your mapping before sending to Elasticsearch:
// Example: Validate documents against expected schema
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });
// Define your expected schema
const expectedSchema = {
title: 'string',
timestamp: 'date',
new_field: 'string',
count: 'number'
};
function validateDocument(doc, schema) {
const errors = [];
// Check for missing required fields
Object.keys(schema).forEach(field => {
if (!(field in doc)) {
errors.push("Missing required field: " + field);
}
});
// Check for unexpected fields
Object.keys(doc).forEach(field => {
if (!(field in schema)) {
errors.push("Unexpected field: " + field);
}
});
return errors;
}
async function safeIndexDocument(indexName, document) {
const validationErrors = validateDocument(document, expectedSchema);
if (validationErrors.length > 0) {
console.error('Document validation failed:', validationErrors);
throw new Error('Invalid document: ' + validationErrors.join(', '));
}
try {
const response = await client.index({
index: indexName,
body: document
});
return response;
} catch (error) {
if (error.meta?.body?.error?.type === 'strict_dynamic_mapping_exception') {
console.error('Strict mapping violation. Update your schema definition.');
// Log the problematic field for debugging
const fieldMatch = error.meta.body.error.reason.match(/[([^]]+)]/);
if (fieldMatch) {
console.error('Field "' + fieldMatch[1] + '" needs to be added to the mapping.');
}
}
throw error;
}
}
// Example usage
await safeIndexDocument('my-index', {
title: 'Test Document',
timestamp: new Date().toISOString(),
new_field: 'test value',
count: 42
});If you need some flexibility but want to maintain control, use dynamic templates:
# Create mapping with dynamic templates
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic_templates": [
{
"strings_as_keywords": {
"match_mapping_type": "string",
"mapping": {
"type": "keyword"
}
}
},
{
"metrics_as_doubles": {
"match": "metric_*",
"mapping": {
"type": "double"
}
}
},
{
"tags_as_keywords": {
"match": "*_tag",
"mapping": {
"type": "keyword"
}
}
}
],
"properties": {
"title": { "type": "text" },
"timestamp": { "type": "date" }
}
}
'
# Dynamic templates allow:
# - All string fields to be indexed as keywords
# - Fields starting with "metric_" to be doubles
# - Fields ending with "_tag" to be keywords
# - Other fields will be rejected (strict mode still applies)
# You can also combine with "dynamic": true but limit field count
curl -X PUT "localhost:9200/my-index/_settings" -u "username:password" -H 'Content-Type: application/json' -d'
{
"index.mapping.total_fields.limit": 1000,
"index.mapping.depth.limit": 20,
"index.mapping.nested_fields.limit": 50
}
'Dynamic templates provide a balance between strict schema enforcement and flexibility for certain field patterns.
## Advanced Mapping Strategies
### Schema Evolution with Reindexing
When your schema needs significant changes, use reindexing:
# Create new index with updated mapping
curl -X PUT "localhost:9200/my-index-v2" -u "username:password" -H 'Content-Type: application/json' -d'
{
"mappings": {
"dynamic": "strict",
"properties": {
"title": { "type": "text" },
"timestamp": { "type": "date" },
"new_field": { "type": "keyword" },
"legacy_field": { "type": "text" } # Keep for backward compatibility
}
}
}
'
# Reindex data with transformation
curl -X POST "localhost:9200/_reindex" -u "username:password" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "my-index"
},
"dest": {
"index": "my-index-v2"
},
"script": {
"source": "// Transform documents during reindexing
if (ctx._source.containsKey('old_field_name')) {
ctx._source.new_field = ctx._source.remove('old_field_name');
}
// Add default values for new fields
if (!ctx._source.containsKey('new_field')) {
ctx._source.new_field = 'default';
}"
}
}
'
# Update aliases
curl -X POST "localhost:9200/_aliases" -u "username:password" -H 'Content-Type: application/json' -d'
{
"actions": [
{
"remove": {
"index": "my-index",
"alias": "current-index"
}
},
{
"add": {
"index": "my-index-v2",
"alias": "current-index"
}
}
]
}
'### Multi-Field Mappings
Define fields with multiple data types for different search use cases:
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"dynamic": "strict",
"properties": {
"product_name": {
"type": "text", # Full-text search
"fields": {
"keyword": {
"type": "keyword", # Exact match, sorting, aggregations
"ignore_above": 256
},
"english": {
"type": "text", # English-specific analysis
"analyzer": "english"
}
}
}
}
}
'### Runtime Fields
Use runtime fields for temporary fields without reindexing:
# Define runtime field in search request
curl -X GET "localhost:9200/my-index/_search" -u "username:password" -H 'Content-Type: application/json' -d'
{
"runtime_mappings": {
"price_with_tax": {
"type": "double",
"script": {
"source": "emit(doc['price'].value * 1.08)"
}
}
},
"query": {
"range": {
"price_with_tax": {
"gte": 100
}
}
}
}
'
# Add runtime field to mapping (Elasticsearch 7.11+)
curl -X PUT "localhost:9200/my-index/_mapping" -u "username:password" -H 'Content-Type: application/json' -d'
{
"runtime": {
"price_with_tax": {
"type": "double",
"script": {
"source": "emit(doc['price'].value * 1.08)"
}
}
}
}
'### Monitoring and Alerting
Set up monitoring for mapping changes:
# Watch for mapping changes
curl -X PUT "localhost:9200/_watcher/watch/mapping-changes" -u "username:password" -H 'Content-Type: application/json' -d'
{
"trigger": {
"schedule": { "interval": "5m" }
},
"input": {
"search": {
"request": {
"indices": [".kibana*"],
"body": {
"query": {
"term": { "type": "index-pattern" }
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": { "gt": 0 }
}
},
"actions": {
"send_email": {
"email": {
"to": ["[email protected]"],
"subject": "Elasticsearch Mapping Changes Detected",
"body": "Field mappings have changed. Review index patterns."
}
}
}
}
'### Best Practices
1. Use "strict" in production to prevent schema drift
2. Maintain mapping documentation for all fields
3. Version your mappings alongside application code
4. Test mapping changes in development first
5. Use index templates for consistency
6. Monitor field counts to prevent mapping explosions
7. Implement schema validation in your data pipeline
QueryShardException: No mapping found for [field] in order to sort on
How to fix "QueryShardException: No mapping found for field in order to sort on" in Elasticsearch
IndexNotFoundException: no such index [index_name]
How to fix "IndexNotFoundException: no such index [index_name]" in Elasticsearch
DocumentMissingException: [index][type][id]: document missing
DocumentMissingException: Document missing
ParsingException: Unknown key for a START_OBJECT in [query]
How to fix "ParsingException: Unknown key for a START_OBJECT in [query]" in Elasticsearch
AggregationExecutionException: Aggregation [agg_name] does not support sampling
How to fix "AggregationExecutionException: Aggregation [agg_name] does not support sampling" in Elasticsearch