{"record":{"id":"8c1ca8fc9b8164f9","repo":"gradle/gradle","slug":"cannot-configure-this-operation-s-once-it-has-s","errorCode":null,"errorMessage":"Cannot configure this operation (%s) once it has started.","messagePattern":"Cannot configure this operation \\((.+?)\\) once it has started\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"platforms/core-runtime/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java","lineNumber":277,"sourceCode":"                throw new IllegalStateException(String.format(\"This operation (%s) has already been started.\", this));\n            }\n            if (state == State.completed) {\n                throw new IllegalStateException(String.format(\"This operation (%s) has already completed.\", this));\n            }\n        }\n\n        private void assertRunning() {\n            if (state == State.idle) {\n                throw new IllegalStateException(String.format(\"This operation (%s) has not been started.\", this));\n            }\n            if (state == State.completed) {\n                throw new IllegalStateException(String.format(\"This operation (%s) has already been completed.\", this));\n            }\n        }\n\n        private void assertCanConfigure() {\n            if (state != State.idle) {\n                throw new IllegalStateException(String.format(\"Cannot configure this operation (%s) once it has started.\", this));\n            }\n        }\n\n    }\n}\n","sourceCodeStart":259,"sourceCodeEnd":283,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/core-runtime/logging/src/main/java/org/gradle/internal/logging/progress/DefaultProgressLoggerFactory.java#L259-L283","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set description (and all other configuration) before started(); treat it as write-once","For mid-run updates use op.progress(\"phase 2 of 3\") — status changes are allowed after start","Compute any dynamic text before creating/starting the operation, or spawn a child operation per phase with its own description"],"exampleFix":"// before\nop.setDescription(\"resolving\");\nop.started();\n...\nop.setDescription(\"downloading\"); // IllegalStateException\n\n// after\nop.setDescription(\"resolving dependencies\");\nop.started();\n...\nop.progress(\"downloading artifacts\"); // status updates only","handlingStrategy":"validation","validationCode":"ProgressLogger configureThenStart(ProgressLoggerFactory factory, String description) {\n    ProgressLogger op = factory.newOperation(MyTask.class);\n    op.setDescription(description); // all configuration BEFORE started()\n    op.started();\n    return op;\n} // afterwards only op.progress(status) is allowed","typeGuard":null,"tryCatchPattern":"try {\n    op.setDescription(newDescription);\n} catch (IllegalStateException e) {\n    if (String.valueOf(e.getMessage()).contains(\"once it has started\")) {\n        op.progress(newDescription); // status updates are the supported mid-run channel\n    } else { throw e; }\n}","preventionTips":["Set description and short description immediately after newOperation, before any lifecycle call","Use progress(status) for mid-run updates instead of reconfiguring","Compute dynamic description text before starting the operation, or use one child operation per phase"],"tags":["gradle","progress-logger","internal-api","lifecycle","immutable-config"],"backgroundTag":"invalid-state-transition","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}