gradle/gradle · error · IllegalStateException

Cannot configure this operation (%s) once it has started.

Error message

Cannot configure this operation (%s) once it has started.

What it means

Configuration methods such as setDescription/setShortDescription guard themselves with assertCanConfigure(), which throws IllegalStateException once the operation's state is no longer idle. After started(), the operation is immutable configuration-wise; mid-run updates must go through progress(status) instead of reconfiguring.

Source

Thrown at platforms/core-runtime/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java:277

                throw new IllegalStateException(String.format("This operation (%s) has already been started.", this));
            }
            if (state == State.completed) {
                throw new IllegalStateException(String.format("This operation (%s) has already completed.", this));
            }
        }

        private void assertRunning() {
            if (state == State.idle) {
                throw new IllegalStateException(String.format("This operation (%s) has not been started.", this));
            }
            if (state == State.completed) {
                throw new IllegalStateException(String.format("This operation (%s) has already been completed.", this));
            }
        }

        private void assertCanConfigure() {
            if (state != State.idle) {
                throw new IllegalStateException(String.format("Cannot configure this operation (%s) once it has started.", this));
            }
        }

    }
}

View on GitHub (pinned to 534f27719b)

Solutions

  1. Set description (and all other configuration) before started(); treat it as write-once
  2. For mid-run updates use op.progress("phase 2 of 3") — status changes are allowed after start
  3. Compute any dynamic text before creating/starting the operation, or spawn a child operation per phase with its own description

Example fix

// before
op.setDescription("resolving");
op.started();
...
op.setDescription("downloading"); // IllegalStateException

// after
op.setDescription("resolving dependencies");
op.started();
...
op.progress("downloading artifacts"); // status updates only
Defensive patterns

Strategy: validation

Validate before calling

ProgressLogger configureThenStart(ProgressLoggerFactory factory, String description) {
    ProgressLogger op = factory.newOperation(MyTask.class);
    op.setDescription(description); // all configuration BEFORE started()
    op.started();
    return op;
} // afterwards only op.progress(status) is allowed

Try / catch

try {
    op.setDescription(newDescription);
} catch (IllegalStateException e) {
    if (String.valueOf(e.getMessage()).contains("once it has started")) {
        op.progress(newDescription); // status updates are the supported mid-run channel
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling op.setDescription("new text") (or other setters) after op.started() — e.g. adjusting the description when a phase changes, or late initialization that runs after the lifecycle already started.

Common situations: Code that computes the description lazily but starts eagerly; phase-based reporting that tries to rewrite the description per phase; race where a background thread configures an operation another thread already started.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/8c1ca8fc9b8164f9. Report an issue: GitHub.