skip to content

Where must input/output annotations be placed on a task class, and how does Gradle's property validation enforce correct declarations?

level: seniorimportance: should knowfreq 35%

answer

  1. @get: use-site target in Kotlin
  2. metadata read from getters
  3. abstract val + Property/RegularFileProperty
  4. validatePlugins task
  5. untracked / conflicting / wrong-type problems

basics

~20 s

Annotations go on the property getter (in Kotlin, use @get:Input etc.). Gradle's property validation checks every public getter is classified as an input, output, or @Internal, and warns (or fails) on untracked or conflicting properties.

solid answer

~40 s

Gradle reads input/output metadata from **getters**, so in Kotlin you must use the `@get:` use-site target — `@get:InputFile`, not a bare `@InputFile` (which would target the field/constructor param and be ignored). Properties are typically `abstract val` backed by Gradle's lazy types (`Property<T>`, `RegularFileProperty`, `ConfigurableFileCollection`), letting Gradle manage instantiation. At runtime (and via the `validatePlugins`/`ValidatePlugins` task) Gradle runs **property validation**: every public getter must be exactly one of an input annotation, an output annotation, or `@Internal`. Unannotated getters, conflicting combinations (e.g. both `@Input` and `@InputFile`), a missing-value non-optional input, or a `@Input` on a `File` produce validation problems — warnings today, and increasingly hard errors. This catches the silent stale-output bugs at build time rather than in production builds.

code

kotlin · 7 lines
kotlin
abstract class ManifestTask : DefaultTask() {
    @get:Input               abstract val appName: Property<String>
    @get:InputFiles          abstract val sources: ConfigurableFileCollection
    @get:OutputFile          abstract val manifest: RegularFileProperty
    @get:Internal            abstract val timestampFormatter: Property<DateTimeFormatter>
}
// `./gradlew validatePlugins` reports any getter that isn't input/output/@Internal

go deeper

for a junior

Know annotations go on getters and in Kotlin you write @get:Input / @get:InputFile.

for a middle

Explain the @get: trap and the use of abstract val with Property/RegularFileProperty.

for a senior

Describe property validation rules and the validatePlugins task as the safety net for correct declarations.

for a principal

Mandate validatePlugins (failing) in CI for all published plugins so input/output defects can't reach consumers.

## Annotations live on getters Gradle inspects the task type's **getter methods** for input/output metadata. In Java you annotate the getter directly. In Kotlin, a property's annotation defaults to the *property/field*, so you must use the `@get:` use-site target: ```kotlin @get:InputFile // correct — targets the getter abstract val template: RegularFileProperty // @InputFile val template ... // WRONG in Kotlin: not on the getter, ignored ``` This is one of the most common Kotlin DSL mistakes — the build compiles but the input is invisible. ## Abstract properties + lazy types Modern task types declare `abstract val` properties of lazy types: - `Property<T>` for values - `RegularFileProperty` / `DirectoryProperty` for files/dirs - `ConfigurableFileCollection` for sets Gradle generates the implementation (managed properties), so you never write backing fields. These integrate with `@get:Input`/`@get:InputFile` cleanly. ## Property validation Gradle validates task and plugin types. Rules include: - **Missing annotation**: a public getter that is none of input/output/`@Internal` → 'is not annotated' problem. - **Conflicting annotations**: e.g. both `@Input` and `@OutputFile` on one getter. - **Wrong type**: `@Input` on a `File`/`FileCollection` → 'has @Input annotation used on property of type File' problem (you wanted `@InputFile`). - **Missing value**: a non-`@Optional` input with no configured value. - **Private getter** carrying an annotation that Gradle can't see. You run this explicitly with the `validatePlugins` task (from the `java-gradle-plugin` plugin), and Gradle also performs lightweight checks during task execution. Validation problems are reported as warnings and, depending on Gradle version/strictness, can fail the build. ## Why it matters Property validation turns 'I forgot `@get:`' from a silent incremental-build corruption into a build-time diagnostic. For plugin authors it's the safety net that keeps published tasks correctly cacheable and incremental.

  • In Kotlin, why does a bare @InputFile (without @get:) fail to register the input?
    Kotlin's default annotation use-site for a property is the field/property, not the getter. Gradle reads metadata from getters, so without @get: the annotation lands somewhere Gradle never inspects and the input is silently untracked.
  • How do you run input/output validation as part of CI?
    Apply the java-gradle-plugin plugin and run the validatePlugins task (or include it in check). It scans task/plugin types and reports problems like untracked, conflicting, or wrongly-typed properties; you can configure it to fail the build.

saying these in an interview costs you the question

  • Putting annotations on fields/constructor params in Kotlin instead of @get:.
  • Assuming validation only documents problems — it can fail the build and prevents silent incrementality bugs.
  • Declaring both a value and a file annotation on the same getter.

context