{"record":{"id":"b2d99fb592373528","repo":"elastic/elasticsearch","slug":"unknown-key-for-a-value-string-in-project-routing-b2d99f","errorCode":null,"errorMessage":"Unknown key for a VALUE_STRING in [project_routing]","messagePattern":"Unknown key for a VALUE_STRING in \\[project_routing\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java","lineNumber":90,"sourceCode":"            request,\n            null,\n            clusterSupportsFeature,\n            size -> searchRequest.source().size(size),\n            null,\n            Optional.of(crossProjectEnabled)\n        );\n\n        // Creates the search template request\n        SearchTemplateRequest searchTemplateRequest;\n        try (XContentParser parser = request.contentOrSourceParamParser()) {\n            searchTemplateRequest = SearchTemplateRequest.fromXContent(parser);\n        }\n\n        if (searchTemplateRequest.getProjectRouting() != null) {\n            if (crossProjectEnabled) {\n                searchRequest.setProjectRouting(searchTemplateRequest.getProjectRouting());\n            } else {\n                throw new IllegalArgumentException(\"Unknown key for a VALUE_STRING in [project_routing]\");\n            }\n        }\n\n        searchTemplateRequest.setRequest(searchRequest);\n        // This param is parsed within the search request\n        if (searchRequest.source().explain() != null) {\n            searchTemplateRequest.setExplain(searchRequest.source().explain());\n        }\n        return channel -> client.execute(\n            MustachePlugin.SEARCH_TEMPLATE_ACTION,\n            searchTemplateRequest,\n            new RestRefCountedChunkedToXContentListener<>(channel) {\n                @Override\n                protected RestStatus getRestStatus(SearchTemplateResponse searchTemplateResponse) {\n                    return searchTemplateResponse.status();\n                }\n            }\n        );","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java#L72-L108","documentation":"Thrown by the single _search/template REST handler when the request body includes a top-level 'project_routing' field but the cluster's cross-project search feature flag (crossProjectEnabled) is off. The field is only honored when the node is configured to allow cross-project routing; otherwise it is rejected as an unknown key rather than silently ignored.","triggerScenarios":"POST to _search/template (or _msearch/template) with a JSON body containing \"project_routing\": \"value\" while the controlling setting/flag for cross-project search is false (default). The handler checks searchTemplateRequest.getProjectRouting() != null && !crossProjectEnabled and throws.","commonSituations":"Enabling a feature in a client library or UI that targets a newer Elasticsearch version than the deployed cluster. Sending requests meant for a stateless/cross-project deployment to a standard cluster. Misreading docs that describe project_routing without noting the feature flag dependency.","solutions":["Remove the 'project_routing' key from the request body if cross-project routing is not needed.","Enable the cross-project search feature on the cluster (set the governing node-level setting) and restart, if the deployment genuinely needs cross-project routing.","Verify the Elasticsearch version supports this field and that the feature flag is actually toggled on via the cluster's settings API before sending.","If routing is the intent, use the standard 'routing' parameter on the search request instead of 'project_routing'."],"exampleFix":"// before\nPOST /_search/template\n{ \"id\": \"t\", \"params\": {}, \"project_routing\": \"projA\" }\n// after (cross-project disabled)\nPOST /_search/template\n{ \"id\": \"t\", \"params\": {} }","handlingStrategy":"validation","validationCode":"// Check feature availability before sending project_routing\nasync function canCrossProject(client) {\n  // Probe cluster settings / feature flag relevant to your deployment\n  const info = await client.info();\n  // Heuristic: only attach project_routing when the deployment advertises cross-project support\n  return Boolean(info.features && info.features.cross_project);\n}\n// then: if (!(await canCrossProject(client))) delete body.project_routing;","typeGuard":null,"tryCatchPattern":"// ElasticsearchStatusException e -> if (e.getMessage().contains('project_routing')) { /* remove field and retry once */ }","preventionTips":["Gate client-side code that adds project_routing behind a feature check.","Document the cluster version/flag requirement next to any code using project_routing.","Prefer the standard 'routing' parameter unless cross-project semantics are required."],"tags":["rest-api","search-template","mustache","cross-project","feature-flag","request-validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}