elastic/elasticsearch · error · IllegalArgumentException
project_routing already set
Error message
project_routing already set
What it means
SearchTemplateRequest.setProjectRouting rejects a second assignment: if the field is already non-null it throws immediately. During XContent parsing the field is declared once via PARSER.declareString(...PROJECT_ROUTING_FIELD), so a duplicate 'project_routing' key in the same JSON object triggers the setter twice and this error.
Source
Thrown at modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateRequest.java:155
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
public Map<String, Object> getScriptParams() {
return scriptParams;
}
public void setScriptParams(Map<String, Object> scriptParams) {
this.scriptParams = scriptParams;
}
public void setProjectRouting(@Nullable String projectRouting) {
if (this.projectRouting != null) {
throw new IllegalArgumentException("project_routing already set");
}
this.projectRouting = projectRouting;
}
@Nullable
public String getProjectRouting() {
return projectRouting;
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (script == null || script.isEmpty()) {
validationException = addValidationError("template is missing", validationException);
}
if (scriptType == null) {
validationException = addValidationError("template's script type is missing", validationException);View on GitHub (pinned to db6a809a66)
Solutions
- Dedupe the request body: ensure 'project_routing' appears at most once per SearchTemplateRequest JSON object.
- In client code, check request.getProjectRouting() == null before calling setProjectRouting, or overwrite the raw field rather than using the guarded setter.
- Validate the serialized JSON with a strict parser/schema that rejects duplicate keys before sending.
Example fix
// before
{"id":"t","project_routing":"a","params":{},"project_routing":"b"}
// after
{"id":"t","project_routing":"a","params":{}} Defensive patterns
Strategy: validation
Validate before calling
// Reject duplicate keys in JSON before sending
function assertNoDuplicateKeys(jsonText) {
JSON.parse(jsonText, (key, value, ctx) => {
// Strict parsers throw on duplicates; alternatively pre-scan raw text
return value;
});
// Robust: count occurrences
const seen = {};
for (const m of jsonText.matchAll(/"([a-zA-Z_][a-zA-Z0-9_]*)"\s*:/g)) {
seen[m[1]] = (seen[m[1]] || 0) + 1;
if (seen[m[1]] > 1) throw new Error(`Duplicate key '${m[1]}' would trigger server-side guard`);
}
} Prevention
- Use a strict JSON serializer that errors on duplicate keys instead of last-wins.
- In Java, replace guarded setters with a clear-on-assign helper or check getProjectRouting()==null first.
- Avoid merging request fragments that both may carry project_routing.
When it happens
Trigger: A _search/template or _msearch/template request body where 'project_routing' appears more than once at the same object level, e.g. {"project_routing":"a", ..., "project_routing":"b"}. Also reachable if application code calls setProjectRouting twice on the same request instance before sending.
Common situations: JSON merge logic that unions two request fragments each carrying project_routing. Templating code that injects the field unconditionally then a caller adds it again. Client-side request builders that set defaults and then overlay user input without checking.
Related errors
- Could not parse inline template
- Malformed search template
- Unknown key for a VALUE_STRING in [project_routing]
- [rest_total_hits_as_int] cannot be used if the tracking of t
- Input does not start with Smile format header
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/50af17a6a99e0fa2.
Report an issue: GitHub.