{"record":{"id":"4edbebaea0ffbef7","repo":"elastic/elasticsearch","slug":"configuration-for-can-not-be-altered-already-l","errorCode":null,"errorMessage":"Configuration for {} can not be altered, already locked","messagePattern":"Configuration for (.+?) can not be altered, already locked","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java","lineNumber":436,"sourceCode":"    public boolean isPreserveDataDir() {\n        return nodes.stream().anyMatch(node -> node.isPreserveDataDir());\n    }\n\n    @Override\n    public void setPreserveDataDir(boolean preserveDataDir) {\n        nodes.all(each -> each.setPreserveDataDir(preserveDataDir));\n    }\n\n    @Override\n    public void freeze() {\n        nodes.forEach(ElasticsearchNode::freeze);\n        configurationFrozen.set(true);\n        nodes.whenObjectAdded(node -> { throw new IllegalStateException(\"Cannot add nodes to test cluster after is has been frozen\"); });\n    }\n\n    private void checkFrozen() {\n        if (configurationFrozen.get()) {\n            throw new IllegalStateException(\"Configuration for \" + this + \" can not be altered, already locked\");\n        }\n    }\n\n    @Override\n    public void start() {\n        commonNodeConfig();\n        nodes.forEach(ElasticsearchNode::start);\n    }\n\n    private void commonNodeConfig() {\n        final String nodeNames;\n        if (nodes.stream().map(ElasticsearchNode::getName).anyMatch(name -> name == null)) {\n            nodeNames = null;\n        } else {\n            nodeNames = nodes.stream().map(ElasticsearchNode::getName).map(this::safeName).collect(Collectors.joining(\",\"));\n        }\n        ElasticsearchNode firstNode = null;\n        for (ElasticsearchNode node : nodes) {","sourceCodeStart":418,"sourceCodeEnd":454,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java#L418-L454","documentation":"Thrown by ElasticsearchCluster.checkFrozen() when any mutating API is invoked after the cluster is locked. checkFrozen() guards configuration setters (e.g. setNumberOfNodes, plugin additions) by reading configurationFrozen; once true, further changes are illegal.","triggerScenarios":"Calling a guarded mutator (setNumberOfNodes, plugin(...), module(...), setting(...), etc.) after freeze() has set configurationFrozen to true.","commonSituations":"Same shape as error 47: task-action-time configuration, late-binding overrides, or hooks running in the execution phase.","solutions":["Move all configuration into the Gradle configuration phase (top-level testClusters {} / node {} blocks).","Compute dynamic values with Provider/Property so they are resolved lazily but set before freeze.","If you must branch on runtime state, use separate cluster definitions rather than mutating a frozen one."],"exampleFix":"// before\ntasks.named('integTest').configure {\n  doFirst { testClusters.cluster.setting('x', 'y') }  // checkFrozen throws\n}\n\n// after\ntestClusters {\n  cluster { setting 'x', providers.gradleProperty('xValue').getOrElse('default') }\n}","handlingStrategy":"validation","validationCode":"// Convention: assert frozen state before mutating\n// if (cluster.isFrozen()) throw ...;\n// In practice: keep all configuration in the testClusters {} DSL block.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reserve testClusters mutation for the configuration phase only.","Use lazy providers for runtime-dependent values.","Define separate cluster specs rather than reconfiguring a frozen one."],"tags":["test","testclusters","lifecycle","configuration"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}