{"record":{"id":"50af17a6a99e0fa2","repo":"elastic/elasticsearch","slug":"project-routing-already-set-50af17","errorCode":null,"errorMessage":"project_routing already set","messagePattern":"project_routing already set","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateRequest.java","lineNumber":155,"sourceCode":"    public String getScript() {\n        return script;\n    }\n\n    public void setScript(String script) {\n        this.script = script;\n    }\n\n    public Map<String, Object> getScriptParams() {\n        return scriptParams;\n    }\n\n    public void setScriptParams(Map<String, Object> scriptParams) {\n        this.scriptParams = scriptParams;\n    }\n\n    public void setProjectRouting(@Nullable String projectRouting) {\n        if (this.projectRouting != null) {\n            throw new IllegalArgumentException(\"project_routing already set\");\n        }\n\n        this.projectRouting = projectRouting;\n    }\n\n    @Nullable\n    public String getProjectRouting() {\n        return projectRouting;\n    }\n\n    @Override\n    public ActionRequestValidationException validate() {\n        ActionRequestValidationException validationException = null;\n        if (script == null || script.isEmpty()) {\n            validationException = addValidationError(\"template is missing\", validationException);\n        }\n        if (scriptType == null) {\n            validationException = addValidationError(\"template's script type is missing\", validationException);","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateRequest.java#L137-L173","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n{\"id\":\"t\",\"project_routing\":\"a\",\"params\":{},\"project_routing\":\"b\"}\n// after\n{\"id\":\"t\",\"project_routing\":\"a\",\"params\":{}}","handlingStrategy":"validation","validationCode":"// Reject duplicate keys in JSON before sending\nfunction assertNoDuplicateKeys(jsonText) {\n  JSON.parse(jsonText, (key, value, ctx) => {\n    // Strict parsers throw on duplicates; alternatively pre-scan raw text\n    return value;\n  });\n  // Robust: count occurrences\n  const seen = {};\n  for (const m of jsonText.matchAll(/\"([a-zA-Z_][a-zA-Z0-9_]*)\"\\s*:/g)) {\n    seen[m[1]] = (seen[m[1]] || 0) + 1;\n    if (seen[m[1]] > 1) throw new Error(`Duplicate key '${m[1]}' would trigger server-side guard`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["request-validation","search-template","mustache","duplicate-key","xcontent"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}