How to check yours
All three are read-only. Set these first:
IDX=http://localhost:9200 # your cluster. Ours had the security plugin off: plain HTTP, no login.
CURL="curl -s" # with security on: CURL="curl -sk -u admin:YOUR_PASSWORD" and IDX=https://...
1. The root cause is in the node log, not in the monitor result
grep -E "Failed to run percolate search|StrictDynamicMappingException" /path/to/opensearch.log | tail -5
# ours ran in Docker: docker logs CONTAINER 2>&1 | grep ...
The monitor result only says Failed to run percolate search. The line you want reads mapping set to strict, dynamic introduction of [FIELD_INDEX_MONITORID] within [OBJECT] is not allowed. OBJECT is the parent object to look at in step 2.
2. Is that object strict, with an array of objects inside?
$CURL "$IDX/YOUR-INDEX/_mapping" | python3 -m json.tool | grep -n -B2 -A6 '"dynamic"'
Look for "dynamic": "strict" on the object the log named, then check whether your documents hold a list of objects under it (in ours, related_incidents was a list of two {"incident", "rule"} objects).
3. What the plugin copied into its queries index
$CURL "$IDX/.opensearch-alerting-queries*/_mapping" | python3 -m json.tool | grep -n '"dynamic"'
Right after we created each monitor, the matching object in the queries index carried the same dynamic value as the source index.
What we measured
Stock OpenSearch 3.8.0, one node, security plugin off, 28 October 2026. For each variant we started clean: deleted the earlier monitors and the .opensearch-alerting-queries* index, created a new source index, created a per document monitor with the query message:error, ran it once with _execute?dryrun=false, wrote one document, and ran it again. The source index mapping, with only dynamic changed between variants:
{ "mappings": { "properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" },
"unmapped": { "properties": {
"platform": {
"dynamic": "strict",
"properties": {
"name": { "type": "keyword" },
"related_incidents": { "properties": {
"incident": { "type": "keyword" },
"rule": { "type": "keyword" } } } } } } } } } }
The document:
{ "@timestamp": "…", "message": "error disk full",
"unmapped": { "platform": { "name": "p1",
"related_incidents": [ { "incident": "INC-1", "rule": "r1" },
{ "incident": "INC-2", "rule": "r2" } ] } } }
dynamic on unmapped.platform | same object in the queries index, after creating the monitor | monitor run after the document |
|---|---|---|
strict | strict | error: Failed to run percolate search for sourceIndex [inc-2299-strict] and queryIndex [.opensearch-alerting-queries-000001] for 1 document(s), 0 documents matched |
false | false | no error, 1 document matched |
true | true | no error, 1 document matched |
For the strict variant the node log had the cause under the same monitor:
StrictDynamicMappingException[mapping set to strict, dynamic introduction of
[related_incidents_inc-2299-strict_<monitorId>] within [unmapped.platform] is not allowed]
at org.opensearch.index.mapper.DocumentParser.parseArray(DocumentParser.java:1119)
The failing step is on the plugin's queries index, .opensearch-alerting-queries-000001: the field name it could not add carries the source index name and the monitor id. That matches the error text in the GitHub issue.
One thing we could not explain
The queries index is shared by all monitors. In a separate run we created the strict monitor, then a second monitor on an index mapped dynamic: true. Reading the queries index mapping back then showed true on that object, yet running the strict monitor again on a new document failed with the same mapping set to strict … within [unmapped.platform]. So the value you read back from the queries index mapping did not tell us whether the strict monitor would fail, and we do not know why. We do not suggest “creating another monitor” as a fix: it did not help.
What made it run (tested on new indices)
Map the parent object with "dynamic": "false" or "dynamic": "true" instead of "strict", then recreate the monitor. Both ran and matched in our test. They are not the same; this is how OpenSearch documents the two settings, and we did not test what happens to unknown fields:
false: unknown fields under that object are kept in_sourcebut not indexed, instead of being rejected when a document is written. Closest to strict in what gets indexed, but you lose the write-time rejection that strict gave you.true: unknown fields are added to the mapping automatically. Any new field under that object becomes part of your index mapping.
We set dynamic when creating each new index. Not measured: changing dynamic on an index that already holds data, deeper nesting, the nested field type, and other OpenSearch versions.
A trap when you test this yourself
A per document monitor only looks at documents written after its previous run. In our first attempt we wrote the test document before creating the monitor: the monitor saw 0 documents, returned no error, and the strict case looked fine. Create the monitor, run it once, then write the test document, then run it again. That is the order the table above used.
For setting up a per document monitor that notifies once per matching line, see one notification per matching document.
Limits of what we measured
Stock OpenSearch 3.8.0 (opensearchproject/opensearch:3.8.0, Alerting 3.8.0.0), one node, security plugin off, plain HTTP, in our test container, 28 October 2026. Monitors were created through the REST API and run on demand with _execute?dryrun=false, not on their schedule, with no actions. One mapping shape (one array of two flat objects under one strict object), one document per variant, one query. Not measured: changing dynamic on an existing index, deeper or nested structures, several source indices on one monitor, a cluster, other versions, and why the strict monitor still failed after the shared queries index mapping read back as true.