A teammate copies application { mainClass.set(...) } into a script plugin and it fails to compile. Why, and what are the workarounds?
answer
- accessors come from plugins {} of compiled script
- apply(from=) not compiled → no accessor
- configure<JavaApplication> { } workaround
- the<T>() / extensions.configure("name")
- real fix = precompiled plugin
basics
~10 sInside an apply(from=) script plugin there are no type-safe accessors, so the application {} accessor doesn't exist. Use configure<JavaApplication> { ... } or the<JavaApplication>() instead, or move the logic into a precompiled plugin.
solid answer
~40 sType-safe accessors like `application { }`, `java { }`, or `sourceSets[...]` are **generated by Gradle from the `plugins {}` block** of a *compiled* script. A plain script plugin applied with `apply(from = ...)` is interpreted, not compiled against the plugin classpath, so those accessors are absent and `application { }` won't resolve. Workarounds: (1) use the generic, reflection-based DSL — `configure<JavaApplication> { mainClass.set("...") }` or `the<JavaApplication>()` to grab the extension by type; (2) use `extensions.configure("application") { ... }` with a string key; (3) the proper fix — promote the logic into a **precompiled script plugin** in `buildSrc`/`build-logic`, where accessors are regenerated, or a real binary plugin. The string/`configure<>()` forms work but are less safe and less discoverable, which is exactly why script plugins don't scale.
code
kotlin · 8 lines// FAILS inside apply(from=) script plugin:
// application { mainClass.set("com.acme.Main") }
// WORKS — generic, reflection-based DSL:
import org.gradle.api.plugins.JavaApplication
configure<JavaApplication> {
mainClass.set("com.acme.Main")
}go deeper
Recognize the error stems from missing type-safe accessors and that configure<>() is the workaround.
Explain why accessors are absent (no compilation against plugin classpath) and list the workarounds.
Use the failure as a migration signal toward precompiled convention plugins; note configure<>() still needs the plugin applied.
Codify a policy that shared config with extensions belongs in convention plugins, avoiding fragile string/configure<>() patterns across teams.
## Why the accessor is missing When you write `plugins { application }` in a **normal build script** (or a precompiled plugin), Gradle: 1. Resolves the plugin and its API to the **plugin classpath**. 2. Generates Kotlin **type-safe accessors** — including an `application { }` block that maps to the `JavaApplication` extension. 3. Compiles your script against that generated API. A **script plugin** applied via `apply(from = "x.gradle.kts")` is *not* part of that compiled, accessor-generating pipeline. It's interpreted as a generic script whose only known type is the apply target (usually `Project`). So `application { }` — an accessor, not a real method on `Project` — simply doesn't exist, and Kotlin compilation fails. ## Workarounds, from quickest to best **1. `configure<T>()` — by type (recommended generic form)** ```kotlin import org.gradle.api.plugins.JavaApplication configure<JavaApplication> { mainClass.set("com.acme.Main") } ``` `configure<T>` looks up the extension of type `T` and configures it. **2. `the<T>()` — fetch the extension instance** ```kotlin the<JavaApplication>().mainClass.set("com.acme.Main") ``` **3. String-keyed `extensions.configure`** ```kotlin extensions.configure<JavaApplication>("application") { /* ... */ } ``` Works but you lose compile-time name checking. **4. The real fix — precompiled plugin** Move the file to `build-logic/src/main/kotlin/my.app-conventions.gradle.kts`, apply `kotlin-dsl`, and now: ```kotlin plugins { application } application { mainClass.set("com.acme.Main") } // accessor works again ``` ## Gotcha: the plugin must already be applied Even `configure<JavaApplication>()` only works if the `application` plugin is actually applied to the target project (e.g. applied earlier in the consuming build, or via `apply(plugin = "application")` inside the script). The accessor limitation is about *syntax sugar*; the extension itself only exists once its plugin is applied. ## Takeaway The failure is a symptom, not a bug: script plugins trade type safety for zero ceremony. Repeated `configure<>()` workarounds are the signal to graduate to a precompiled convention plugin.
- Does configure<JavaApplication> {} work if the application plugin isn't applied?No. The extension only exists after its plugin is applied. configure<>() restores the syntax but not the extension itself — you still need apply(plugin = "application") (or it applied in the consuming build) first.
- Why is the string-keyed form less desirable than configure<T>()?The string name ("application") isn't checked at compile time, so a typo fails only at runtime; configure<T>() resolves by type and is safer and more refactor-friendly.
saying these in an interview costs you the question
- Saying the fix is to import the accessor — accessors are generated, not importable in a script plugin.
- Forgetting that the extension's plugin must be applied for configure<>() to find anything.