Cannot apply plugins to subprojects from within a settings plugin

After updating Gradle from 8 to 9, the plugin stopped working. I now get the error:

Execution failed for task ‘:extensions:nunl:processReleaseResources’.

Cannot mutate the dependencies of configuration ‘:extensions:nunl:releaseCompileClasspath’ after the configuration was resolved. After a configuration has been observed, it should not be modified.

I cannot pinpoint what causes it.

The plugin I am writing works in this fashion:

A settings plugin applies plugins to subprojects respectively. These plugins then configure dependencies and share artifacts to each other. Namely, “extension” projects supply artifacts to the single “patches” project. Related code:

private fun Settings.configureProjects(extension: SettingsExtension) {
    // region Include the projects

    val extensionsProjectPath = extension.extensions.projectsPath

    if (extensionsProjectPath != null) {
        objectFactory.fileTree().from(rootDir.resolve(extensionsProjectPath)).matching {
            it.include("**/build.gradle.kts", "**/build.gradle")
        }.forEach {
            include(it.parentFile.relativeTo(rootDir).toPath().joinToString(":"))
        }
    }

    include(extension.patchesProjectPath)

    // endregion

    // region Apply the plugins

    gradle.rootProject { rootProject ->
        if (extensionsProjectPath != null) {
            val extensionsProject = try {
                rootProject.project(extensionsProjectPath)
            } catch (e: UnknownProjectException) {
                null
            }

            extensionsProject?.subprojects { extensionProject ->
                if (
                    extensionProject.buildFile.exists() &&
                    !extensionProject.parent!!.plugins.hasPlugin(ExtensionPlugin::class.java)
                ) {
                    extensionProject.pluginManager.apply(ExtensionPlugin::class.java)
                }
            }
        }

        // Needs to be applied after the extension plugin
        // so that their extensionConfiguration is available for consumption.
        rootProject.project(extension.patchesProjectPath).pluginManager.apply(PatchesPlugin::class.java)
    }   

The patches plugin:

/**
 * Configures the project to consume the extension artifacts and add them to the resources of the patches project.
 */
private fun Project.configureConsumeExtensions(patchesExtension: PatchesExtension) {
    val extensionsProject = try {
        project(patchesExtension.extensionsProjectPath ?: return)
    } catch (e: UnknownProjectException) {
        return
    }

    val extensionProjects = extensionsProject.subprojects.filter { extensionProject ->
        extensionProject.plugins.hasPlugin(ExtensionPlugin::class.java)
    }

    val extensionsDependencyScopeConfiguration =
        configurations.dependencyScope("extensionsDependencyScope").get()
    val extensionsConfiguration = configurations.resolvable("extensionConfiguration").apply {
        configure { it.extendsFrom(extensionsDependencyScopeConfiguration) }
    }

    project.dependencies.apply {
        extensionProjects.forEach { extensionProject ->
            add(
                extensionsDependencyScopeConfiguration.name,
                project(
                    mapOf(
                        "path" to extensionProject.path,
                        "configuration" to "extensionConfiguration",
                    ),
                ),
            )
        }
    }

    extensions.configure<SourceSetContainer>("sourceSets") { sources ->
        sources.named("main") { main ->
            main.resources.srcDir(extensionsConfiguration)
        }
    }
}

The extension plugin:

private fun Project.configureArtifactSharing(extension: ExtensionExtension) {
    val androidExtension = extensions.getByType<BaseAppModuleExtension>()
    val syncExtensionTask = tasks.register<Sync>("syncExtension") {
        val dexTaskName = if (androidExtension.buildTypes.getByName("release").isMinifyEnabled) {
            "minifyReleaseWithR8"
        } else {
            "mergeDexRelease"
        }

        val dexTask = tasks.getByName(dexTaskName)

        dependsOn(dexTask)

        val extensionName = if (extension.name != null) {
            Path(extension.name!!)
        } else {
            projectDir.resolveSibling(project.name + ".rve").relativeTo(rootDir).toPath()
        }

        from(dexTask.outputs.files.asFileTree.matching { include("**/*.dex") })
        into(layout.buildDirectory.dir("revanced/${extensionName.parent.pathString}"))

        rename { extensionName.fileName.toString() }
    }

    configurations.create("extensionConfiguration").apply {
        isCanBeResolved = false
        isCanBeConsumed = true

        outgoing.artifact(layout.buildDirectory.dir("revanced")) {
            it.builtBy(syncExtensionTask)
        }
    }
}

Mind that there are likely bad practices involved, which could also be the source of the issue; however, I did not find any other way to achieve the behaviour I had with this in Gradle 8. Ideally, I can restore the behaviour I saw on Gradle 8.

I did not review all your code, but if you get that error you mentioned, that is just the symptom.
Something resolved the configuration you try to configure before you try to configure it.
So you would need to find out / have a breakpoint when the configuration you try to modify is locked by being resolved.
Then you can check whether that is a legit resolve.

This problem often arises because something resolves a configuration at configuration time which is seldomly a good idea.

When I omit the call to configureConsumeExtensions, the symptom no longer occurs. I will try to use breakpoints to find the relevant lines. Maybe a stacktrace could show where the issue occurs? I don’t think a breakpoint would show which line causes the error, because the stack trace didn’t either

Yes, it would, if you have read what I wrote closely. :wink:

The error happens because you try to add some dependency to a configuration that already took part in dependency resolution, which in Gradle 9 was made a hard error.

But this error is usually just the symptom, which is also why the stacktrace is not helping in any way, and also a breakpoint at the spot where it fails would of course not help, but that is not what I said.

I said that most often the actual problem is, that someone already resolved the configuration prematurely, for example at configuration time and before you try to add dependencies. So you have to use breakpoints to find the spot that resolves the configuration and thus locks it for change.

Do I set the breakpoints anywhere in my Gradle plugin? How do I know whether the configuration resolves at the breakpoint? Is there one configuration or multiple? How do I see the configuration and if it is resolved? Is there an API to get the configuration when I break?

No, of course not in your plugin, you don’t know what causes the resolve, so that would be pointless.
Find the condition in the Gradle code when the exception that is thrown is triggered.
Find where this condition is turned to being true.
Set a breakpoint there to see when the configuration is resolved.

Oh wait, your error says “Execution failed for task ‘:extensions:nunl:processReleaseResources’.”, so you are in the execution phase already.

Forget what I said, it seems to not be the typical case but really your error.
You should not change any configuration options at task execution time.

How do I know which configuration I am changing at execution time? I have to share an artifact between modules which happens after one project built it. How do determine which part of the code in the plugin is responsible for changing the configuration?

To share an artifact, create an outgoing variant for it and depend on that variant from the other projects. You must not change the configuration at execution time for that.

As far as I am concerned, I do that. How do I know that I am not doing that and the error is actually caused by it? Note when I remove the call to consume the artifact, the error disappears, maybe thats an indicator

From last time I checked the docs, it has changed a little. I’ll give replicating it a shot and see if that happens to resolve the issue: How to Share Artifacts Between Projects with Gradle

I skimmed over your code now.
As you said, there are some bad practices (doing cross-project configuration, doing cross-project mutable state reading, using configuration = instead of attributes to consume the artifact from the other project, requiring a specific plugin application order, using .plugins, …), but no idea whether those cause it or whether you maybe are following a red herring.

Your error says it fails at processReleaseResources, and if you look at the --stacktrace you should also see where exactly. That should hopefully give an indication where the problem stems from.

I have checked the stacktrace, but I cant draw connections to the cause from the internal code

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':extensions:extension:processReleaseResources'.
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.DefaultNodeExecutor.executeLocalTaskNode(DefaultNodeExecutor.java:55)
	at org.gradle.execution.plan.DefaultNodeExecutor.execute(DefaultNodeExecutor.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:339)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:84)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:339)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:328)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:459)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:376)
	at org.gradle.execution.plan.DefaultPlanExecutor.process(DefaultPlanExecutor.java:111)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.executeWithServices(DefaultTaskExecutionGraph.java:146)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.execute(DefaultTaskExecutionGraph.java:131)
	at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:35)
	at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:54)
	at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.call(BuildOperationFiringBuildWorkerExecutor.java:43)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.execution.BuildOperationFiringBuildWorkerExecutor.execute(BuildOperationFiringBuildWorkerExecutor.java:40)
	at org.gradle.internal.build.DefaultBuildLifecycleController.lambda$executeTasks$10(DefaultBuildLifecycleController.java:313)
	at org.gradle.internal.model.StateTransitionController.doTransition(StateTransitionController.java:276)
	at org.gradle.internal.model.StateTransitionController.lambda$tryTransition$8(StateTransitionController.java:187)
	at org.gradle.internal.work.DefaultSynchronizer.withLock(DefaultSynchronizer.java:45)
	at org.gradle.internal.model.StateTransitionController.tryTransition(StateTransitionController.java:187)
	at org.gradle.internal.build.DefaultBuildLifecycleController.executeTasks(DefaultBuildLifecycleController.java:304)
	at org.gradle.internal.build.DefaultBuildWorkGraphController$DefaultBuildWorkGraph.runWork(DefaultBuildWorkGraphController.java:220)
	at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
	at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
	at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
	at org.gradle.composite.internal.DefaultBuildController.doRun(DefaultBuildController.java:181)
	at org.gradle.composite.internal.DefaultBuildController.access$000(DefaultBuildController.java:50)
	at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.lambda$run$0(DefaultBuildController.java:198)
	at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:84)
	at org.gradle.composite.internal.DefaultBuildController$BuildOpRunnable.run(DefaultBuildController.java:198)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
Caused by: org.gradle.api.InvalidUserCodeException: Cannot mutate the dependencies of configuration ':extensions:extension:releaseCompileClasspath' after the configuration was resolved. After a configuration has been observed, it should not be modified.
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.validateMutation(DefaultConfiguration.java:1192)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$validateMutationType$0(DefaultConfiguration.java:282)
	at org.gradle.internal.ImmutableActionSet$SingletonSet.execute(ImmutableActionSet.java:226)
	at org.gradle.api.internal.DefaultDomainObjectCollection.assertCanMutate(DefaultDomainObjectCollection.java:453)
	at org.gradle.api.internal.DefaultDomainObjectCollection.add(DefaultDomainObjectCollection.java:273)
	at org.gradle.api.internal.DelegatingDomainObjectSet.add(DelegatingDomainObjectSet.java:115)
	at org.gradle.api.internal.artifacts.DefaultDependencyConstraintSet.addInternalDependencyConstraint(DefaultDependencyConstraintSet.java:59)
	at org.gradle.api.internal.artifacts.DefaultDependencyConstraintSet.add(DefaultDependencyConstraintSet.java:54)
	at org.gradle.api.internal.artifacts.DefaultDependencyConstraintSet.add(DefaultDependencyConstraintSet.java:30)
	at org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyConstraintHandler.doAdd(DefaultDependencyConstraintHandler.java:198)
	at org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyConstraintHandler.add(DefaultDependencyConstraintHandler.java:134)
	at com.android.build.gradle.internal.dependency.ConstraintHandler$alignWith$1$1.execute(ConstraintHandler.kt:57)
	at com.android.build.gradle.internal.dependency.ConstraintHandler$alignWith$1$1.execute(ConstraintHandler.kt:49)
	at org.gradle.api.internal.artifacts.result.DefaultResolvedComponentResult.eachElement(DefaultResolvedComponentResult.java:191)
	at org.gradle.api.internal.artifacts.result.DefaultResolutionResult.allDependencies(DefaultResolutionResult.java:81)
	at com.android.build.gradle.internal.dependency.ConstraintHandler$alignWith$1.execute(ConstraintHandler.kt:49)
	at com.android.build.gradle.internal.dependency.ConstraintHandler$alignWith$1.execute(ConstraintHandler.kt:43)
	at org.gradle.internal.code.DefaultUserCodeApplicationContext$CurrentApplication$1.execute(DefaultUserCodeApplicationContext.java:124)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:101)
	at org.gradle.internal.event.BroadcastDispatch$ActionInvocationHandler.dispatch(BroadcastDispatch.java:89)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:44)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:270)
	at org.gradle.internal.event.BroadcastDispatch$SingletonDispatch.dispatch(BroadcastDispatch.java:172)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:84)
	at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:70)
	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:382)
	at org.gradle.internal.event.BroadcastDispatch$CompositeDispatch.dispatch(BroadcastDispatch.java:274)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:160)
	at org.gradle.internal.event.ListenerBroadcast.dispatch(ListenerBroadcast.java:37)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:88)
	at jdk.proxy1/jdk.proxy1.$Proxy70.beforeResolve(Unknown Source)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:627)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$1.call(DefaultConfiguration.java:623)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphInBuildOperation(DefaultConfiguration.java:623)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.lambda$resolveExclusivelyIfRequired$3(DefaultConfiguration.java:615)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$CalculatedModelValueImpl.update(DefaultProjectStateRegistry.java:546)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveExclusivelyIfRequired(DefaultConfiguration.java:610)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.resolveGraphIfRequired(DefaultConfiguration.java:603)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.access$900(DefaultConfiguration.java:142)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ResolverResultsResolutionResultProvider.getValue(DefaultConfiguration.java:585)
	at org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$ResolverResultsResolutionResultProvider.getValue(DefaultConfiguration.java:571)
	at org.gradle.api.internal.artifacts.configurations.ResolutionResultProvider$1.getValue(ResolutionResultProvider.java:54)
	at org.gradle.api.internal.artifacts.configurations.ResolutionResultProviderBackedSelectedArtifactSet.visitArtifacts(ResolutionResultProviderBackedSelectedArtifactSet.java:50)
	at org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet.visitFiles(SelectedArtifactSet.java:34)
	at org.gradle.api.internal.artifacts.configurations.ResolutionBackedFileCollection.visitContents(ResolutionBackedFileCollection.java:75)
	at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:361)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.lambda$calculateFinalizedValue$1(DefaultConfigurableFileCollection.java:423)
	at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:66)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection$UnresolvedItemsCollector.visitContents(DefaultConfigurableFileCollection.java:635)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.calculateFinalizedValue(DefaultConfigurableFileCollection.java:423)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.finalizeNow(DefaultConfigurableFileCollection.java:125)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.lambda$visitChildren$2(DefaultConfigurableFileCollection.java:447)
	at org.gradle.api.internal.provider.ValueState.finalizeOnReadIfNeeded(ValueState.java:142)
	at org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection.visitChildren(DefaultConfigurableFileCollection.java:447)
	at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:112)
	at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:361)
	at org.gradle.api.internal.file.CompositeFileCollection.lambda$visitContents$0(CompositeFileCollection.java:112)
	at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:66)
	at org.gradle.api.internal.file.collections.UnpackingVisitor.add(UnpackingVisitor.java:91)
	at org.gradle.api.internal.file.DefaultFileCollectionFactory$ResolvingFileCollection.visitChildren(DefaultFileCollectionFactory.java:311)
	at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:112)
	at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:361)
	at org.gradle.api.internal.file.CompositeFileCollection.lambda$visitContents$0(CompositeFileCollection.java:112)
	at org.gradle.api.internal.tasks.PropertyFileCollection.visitChildren(PropertyFileCollection.java:48)
	at org.gradle.api.internal.file.CompositeFileCollection.visitContents(CompositeFileCollection.java:112)
	at org.gradle.api.internal.file.AbstractFileCollection.visitStructure(AbstractFileCollection.java:361)
	at org.gradle.internal.fingerprint.impl.DefaultFileCollectionSnapshotter.snapshot(DefaultFileCollectionSnapshotter.java:48)
	at org.gradle.internal.execution.impl.DefaultInputFingerprinter$InputCollectingVisitor.visitInputFileProperty(DefaultInputFingerprinter.java:151)
	at org.gradle.api.internal.tasks.execution.TaskExecution.visitMutableInputs(TaskExecution.java:340)
	at org.gradle.internal.execution.impl.DefaultInputFingerprinter.fingerprintInputProperties(DefaultInputFingerprinter.java:77)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.captureExecutionStateWithOutputs(AbstractCaptureStateBeforeExecutionStep.java:123)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.lambda$captureExecutionState$0(AbstractCaptureStateBeforeExecutionStep.java:88)
	at org.gradle.internal.execution.steps.BuildOperationStep$1.call(BuildOperationStep.java:38)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
	at org.gradle.internal.execution.steps.BuildOperationStep.operation(BuildOperationStep.java:35)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.captureExecutionState(AbstractCaptureStateBeforeExecutionStep.java:83)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:66)
	at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:46)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:133)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:59)
	at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:36)
	at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
	at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.executeMutable(HandleStaleOutputsStep.java:77)
	at org.gradle.internal.execution.steps.HandleStaleOutputsStep.executeMutable(HandleStaleOutputsStep.java:43)
	at org.gradle.internal.execution.steps.MutableStep.execute(MutableStep.java:25)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
	at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:305)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
	at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
	at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
	at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
	at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
	at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:31)
	at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:68)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:132)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
	at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	... 54 more

You previously mentioned:

So you would need to find out / have a breakpoint when the configuration you try to modify is locked by being resolved.

Do I just set breakpoints in the Gradle plugin and check which line doesn’t run? How would i defer the resolution of the configuration after configuring it?

Ok, the stacktrace shows it is not really during execution time, it is while fingerprinting the inputs of the task, executing a before-resolve action added by AGP, so it seems to not be you changing config at execution time, but most probably back to the typical case described above and also how to investigate further.

So you would need to find out / have a breakpoint when the configuration you try to modify is locked by being resolved.

Do I just set breakpoints in the Gradle plugin and check which line doesn’t run? How would i defer the resolution of the configuration after configuring it?

Do you intentionally not listen?
You asked exactly this question in Cannot apply plugins to subprojects from within a settings plugin - #6 by oSumAtrIX with different words and I already answered too in the following comment.

Maybe it is also a problem of AGP.
That specific code that triggers the error is only present until 8.2.x and gone with 8.3+.
As I don’t do Android I have no idea whether that was indeed bad code in AGP that was fixed, or whether updating will just treat the symptom, not triggering the error. :man_shrugging:

Yes, I intentionally do not listen. On a serious note, though, it has been a while since I asked the question, and naturally, I didn’t remember what I said and skimmed over the messages here, accidentally skipping over mine.

Ok, the stacktrace shows it is not really during execution time, it is while fingerprinting the inputs of the task, executing a before-resolve action added by AGP, so it seems to not be you changing config at execution time, but most probably back to the typical case described above and also how to investigate further.

AGP works fine when used as a plugin. I also do not know which part of my plugin is interacting with AGP that causes the issue yet.

Find the condition in the Gradle code when the exception that is thrown is triggered.
Find where this condition is turned to being true.
Set a breakpoint there to see when the configuration is resolved.

How do I see when the configuration is resolved? I set a breakpoint at the condition when the exception that is thrown is triggered:

I checked the surrounding code as well as the stack frames, but I don’t know how I can see when the configuration is resolved. Also, what does it tell me when it is resolved? Does it show the order of configurations resolved, and when this one is resolved, how do I make it mutate before it has been observed by Gradle?

AGP works on its own, but the plugin I wrote using it does not. Gradle 8.3+ enforced this issue to be an error, so it was an issue in 8.2 too; Gradle just didn’t treat it as an error.

As I don’t do Android I have no idea whether that was indeed bad code in AGP that was fixed, or whether updating will just treat the symptom, not triggering the error.

I tried the latest version of AGP v9.0.0, but the issue still exists. The plugin does work when used normally. In my plugin i mirrored what I would do when I use it regularly, but in my plugin, it does raise this error, so it must be an issue on my end and not AGP.

Given that, why would it matter if you do or know Android or not? Because I do know Android, and yet this knowledge doesn’t get me anywhere with this Gradle issue. Shouldn’t this be something in Gradle’s business?

I didn’t remember what I said and skimmed over the messages here

I’m just a fellow user like you, helping other users in my sparse spare time, not a paid supporter or similar.
So for me it just is part of good manners that you try to not waste my precious time by asking the same question again, I also had to reread the whole thread thoroughly to know what we were talking about 2 months ago. :wink:

How do I see when the configuration is resolved? I set a breakpoint at the condition when the exception that is thrown is triggered:

That is still just looking at the symptom. You need to have the breakpoint where the condition that is checked there is changed as I said multiple times, so that you see when and why the configuration is marked as already observed, so the breakpoint should be in markAsObserved(...) where observationReason is assigned, having a condition for the name of the configuration from the error message, so that you see when this configuration was observed.

Also, what does it tell me when it is resolved?

Really, reread the thread, I already explained it.
The most typical problem with this error is, that something prematurely resolves a configuration at configuration phase and after that something tries to mutate the configuration. The “something” in your case is AGP, but that is just the symptom in that most typical case. And if you break on the configuration getting resolved / observed, you see where this is coming from, to eventually find the location where something is prematurely resolving the configuration badly.

Does it show the order of configurations resolved,

If you add logging to that breakpoint, you could probably log that, yes.

and when this one is resolved,

Yes, that’s the point.

how do I make it mutate before it has been observed by Gradle?

If it is the most typical case, you make sure to not prematurely resolve the configuration too early.

AGP works on its own, but the plugin I wrote using it does not.

That dose not mean that there is not a problem in AGP theoretically, that is just triggered by the conditions created by your plugin. But I would still first eradicate the most typical case.

Given that, why would it matter if you do or know Android or not?

It could well be that the AGP version you are using is not compatible with Gradle 9, especially as that exact code was changed in later AGP versions. So if it still happens with newer AGP version, at least the stacktrace changed. I did not yet found a compatibility table of Gradle versions supported by AGP versions (except for minimum version) and I don’t do Android, so I don’t know whether this might be the case, that the AGP version is not compatible with the Gradle version, as I don’t have experience in using it. That’s why it is relevant that I don’t do Android development.

Thanks for your time. I can see a couple of things that could’ve shaved some back and forth. Thanks for helping.

Here is the breakpoint:

It comes from an internal method:

How do I know the location I am looking at is prematurely resolving the configuration? The stack frames do not contain any method from my plugin and from skimming the stack frame I also do not see anything related to the keywords “resolve” apart from these:

If you add logging to that breakpoint, you could probably log that, yes.

The only order I saw is the same as what Gradle already outputs to stdout, I can’t see anything new.

you make sure to not prematurely resolve the configuration too early.

Do I omit a call to a function called resolve or set a field resolve to false in my plugins code? The only reference to something called resolve is this:

and its already set to false.

Isn’t the most typical case that my plugin causes the error? and hence AGP is a question for later?

It could well be that the AGP version you are using is not compatible with Gradle 9

In fact, the AGP version I use mandates Gradle 9 as a minimum:

I use AGP 9.

I did not yet found a compatibility table of Gradle versions supported by AGP versions (except for minimum version)

All that matters is the minimum version. I use Gradle 9.3.1, and AGP is compatible with that version (I also tried the exact version Gradle 9.1).

Here is the breakpoint

You should have checked conf.name in case an extending configuration causes the observing, but as it was hit, it probably is good enough.

It comes from an internal method:

I don’s see that from just those 5 lines in the screenshot, the reason is not visible there.

skimming the stack frame I also do not see anything related to the keywords “resolve” apart from these:

Searching for “resolve” is useless :slight_smile:

How do I know the location I am looking at is prematurely resolving the configuration? The stack frames do not contain any method from my plugin

If there are only Gradle stackframes, maybe it is not that typical case, or there is a bug in Gradle, hard to say without deeper debugging.

Maybe you should report it as a bug, or maybe you could comment out all code in your plugin, commenting in part by part to see when the error pops up to maybe then see what the culprit is or how to reproduce the bug if it is a Gradle bug.

The only order I saw is the same as what Gradle already outputs to stdout, I can’t see anything new.

I didn’t say you will see anything new, you just asked how you could output the order :slight_smile:

Do I omit a call to a function called resolve or set a field resolve to false in my plugins code?

Only in rare cases there will be anything involved actually being called “resolve”.
That isCanBeResolved surely is not relevant.

Btw. you should not create a configuration but register it.
Most domain containers in Gradle are not treated lazily now, but could in the future,
and tasks and configurations indeed are treated lazily by Gradle already.
Best is to always use lazy API and never eager unless you have a really good reason to.
But that is just a sidenote and most probably not related to the problem at hand.

Isn’t the most typical case that my plugin causes the error? and hence AGP is a question for later?

Yes, and that stacktrace should have shown where.
So maybe it is a less typical case. :man_shrugging:

In fact, the AGP version I use mandates Gradle 9 as a minimum:
I use AGP 9.

You did not at the moment you posted the first stacktrace here in Cannot apply plugins to subprojects from within a settings plugin - #13 by oSumAtrIX as there you used AGP <8.3, otherwise the stacktrace wouldn’t fit.

All that matters is the minimum version

… no, not in general
minimum version matters the same as maximium version does.
If you try to use an AGP version of 3 years ago with a Gradle version of today, it will most probably not work because API in Gradle that is used by AGP changed.

Solved it by using the new extension from AGP v9 instead of the now-deprecated one from AGP v8 (ApplicationExtension instead of BaseAppModuleExtension).

1 Like