elastic/elasticsearch · error · IllegalStateException

Configuration for {} can not be altered, already locked

Error message

Configuration for {} can not be altered, already locked

What it means

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.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java:436

    public boolean isPreserveDataDir() {
        return nodes.stream().anyMatch(node -> node.isPreserveDataDir());
    }

    @Override
    public void setPreserveDataDir(boolean preserveDataDir) {
        nodes.all(each -> each.setPreserveDataDir(preserveDataDir));
    }

    @Override
    public void freeze() {
        nodes.forEach(ElasticsearchNode::freeze);
        configurationFrozen.set(true);
        nodes.whenObjectAdded(node -> { throw new IllegalStateException("Cannot add nodes to test cluster after is has been frozen"); });
    }

    private void checkFrozen() {
        if (configurationFrozen.get()) {
            throw new IllegalStateException("Configuration for " + this + " can not be altered, already locked");
        }
    }

    @Override
    public void start() {
        commonNodeConfig();
        nodes.forEach(ElasticsearchNode::start);
    }

    private void commonNodeConfig() {
        final String nodeNames;
        if (nodes.stream().map(ElasticsearchNode::getName).anyMatch(name -> name == null)) {
            nodeNames = null;
        } else {
            nodeNames = nodes.stream().map(ElasticsearchNode::getName).map(this::safeName).collect(Collectors.joining(","));
        }
        ElasticsearchNode firstNode = null;
        for (ElasticsearchNode node : nodes) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Move all configuration into the Gradle configuration phase (top-level testClusters {} / node {} blocks).
  2. Compute dynamic values with Provider/Property so they are resolved lazily but set before freeze.
  3. If you must branch on runtime state, use separate cluster definitions rather than mutating a frozen one.

Example fix

// before
tasks.named('integTest').configure {
  doFirst { testClusters.cluster.setting('x', 'y') }  // checkFrozen throws
}

// after
testClusters {
  cluster { setting 'x', providers.gradleProperty('xValue').getOrElse('default') }
}
Defensive patterns

Strategy: validation

Validate before calling

// Convention: assert frozen state before mutating
// if (cluster.isFrozen()) throw ...;
// In practice: keep all configuration in the testClusters {} DSL block.

Prevention

When it happens

Trigger: Calling a guarded mutator (setNumberOfNodes, plugin(...), module(...), setting(...), etc.) after freeze() has set configurationFrozen to true.

Common situations: Same shape as error 47: task-action-time configuration, late-binding overrides, or hooks running in the execution phase.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/4edbebaea0ffbef7. Report an issue: GitHub.