{"record":{"id":"19cc3518349f566e","repo":"elastic/elasticsearch","slug":"configuration-for-can-not-be-altered-already-l-19cc35","errorCode":null,"errorMessage":"Configuration for {} can not be altered, already locked","messagePattern":"Configuration for (.+?) can not be altered, already locked","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java","lineNumber":1551,"sourceCode":"            gcLogSub = new ReplacementKey(\"logs/gc.log\", \"gc.log\");\n        }\n        expansions.put(gcLogSub, confPathLogs.resolve(\"gc.log\").toString());\n\n        ReplacementKey errorFileSub;\n        if (version.before(\"8.19.0\") && version.getMajor() >= 7) {\n            errorFileSub = new ReplacementKey(\"-XX:ErrorFile=logs/hs_err_pid%p.log\", null);\n        } else {\n            // temporarily check the old substitution first so both old and new work during backport\n            errorFileSub = new ReplacementKey(\"-XX:ErrorFile=logs/hs_err_pid%p.log\", \"-XX:ErrorFile=hs_err_pid%p.log\");\n        }\n        expansions.put(errorFileSub, \"-XX:ErrorFile=\" + confPathLogs.resolve(\"hs_err_pid%p.log\"));\n\n        return expansions;\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    private List<String> getTransportPortInternal() {\n        try {\n            return readPortsFile(transportPortFile);\n        } catch (IOException e) {\n            throw new UncheckedIOException(\"Failed to read transport ports file: \" + transportPortFile + \" for \" + this, e);\n        }\n    }\n\n    private List<String> getHttpPortInternal() {\n        try {\n            return readPortsFile(httpPortsFile);\n        } catch (IOException e) {\n            throw new UncheckedIOException(\"Failed to read http ports file: \" + httpPortsFile + \" for \" + this, e);\n        }\n    }","sourceCodeStart":1533,"sourceCodeEnd":1569,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java#L1533-L1569","documentation":"Thrown by checkFrozen() when any configuration-mutating method is called after configurationFrozen AtomicBoolean has been set to true. The plugin freezes the node config once the cluster has started building/starting to prevent races where settings change mid-run.","triggerScenarios":"A test or build script calls node.setting(...), setHttpPort(...), setDataPath(...), keystorePassword(...), or similar setters after the node has already started (or after the RunTask has begun its setup loop and frozen the config).","commonSituations":"A doFirst / doLast block in build.gradle that mutates the node at the wrong lifecycle phase; calling additionalSettings.forEach(node::setting) after cluster start; a custom task subclassing RunTask that configures nodes too late.","solutions":["Move all node.configuration calls into the Gradle configuration phase (the body of the task definition or a configuration block), not into an action that runs at execution time.","Use additionalSettings on RunTask (applied before freeze) rather than mutating nodes directly inside an action.","Check the stack trace to find which setter was called after freeze and reorder it."],"exampleFix":"// before: mutates after start\ntask myRun(type: RunTask) {\n    doFirst { cluster.getNodes().each { it.setting('x', 'y') } }  // throws\n}\n// after: configure in configuration phase\ntask myRun(type: RunTask) {\n    additionalSettings = ['x':'y']\n}","handlingStrategy":"validation","validationCode":"if (configurationFrozen.get()) {\n    throw new IllegalStateException(\"Config already frozen; mutate in configuration phase\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Configure nodes in the Gradle configuration phase, not in doFirst/doLast actions.","Use RunTask's additionalSettings rather than direct node mutation.","Check the stack trace to find the late setter and reorder it."],"tags":["testclusters","lifecycle","api-misuse","gradle"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}