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.