This page summarizes the different task (or in general work) validation problems that Gradle reports and provides guidance for fixing them.

Invalid use of cacheable annotation

This error indicates that you have annotated a type with @CacheableTransform on something that is not an artifact transform, or @CacheableTask on something which is not a Task.

The solution is to remove the annotation.

For tasks, the annotation to use is @CacheableTask. For artifact transforms, the annotation to use is @CacheableTransform

Missing normalization annotation

This error occurs whenever a task or artifact transform is cacheable and that a file or file collection input property isn’t declaring how it should be normalized. Normalization tells Gradle if, for example, the absolute path of input files matter or if only the contents is relevant. If you don’t declare the normalization strategy, outputs of the task cannot be reused between machines or between different locations on the same machine. In short, without normalization, caching is highly ineffective.

To fix this problem, you need to declare a normalization strategy, by applying one of the following annotations:

Required value isn’t set

This error indicates that a property expected a value but none was provided. By default, Gradle properties are required, that is to say that if an input or output property isn’t configured, either via a conventional value or explicitly by a build script, then Gradle will fail because it doesn’t know what value to use when the task is executed.

To fix the problem, you must either:

  • provide a value for this property explicitly (for example by configuring the task in your build script)

  • or make the property optional by annotating it with @Optional

Invalid use of absolute path sensitivity for an artifact transform

This error indicates that you have annotated an input of a cacheable artifact transform as sensitive to the absolute path. However, artifact transforms are executed in isolation using their own workspace which, for example, is resilient to clean builds. Even if the artifact transform result can’t be shared via the build cache, using an absolute path sensitivity doesn’t make sense.

To fix this problem, you must change the normalization strategy using one of those:

Invalid use of an output property on an artifact transforms

This error indicates that you have annotated a property of an artifact transform with an output annotation, which is not the correct way to register outputs for artifact transforms.

To fix this problem, you must remove the property and use the TransformOutputs parameter instead.

Invalid use of annotations on fields

This error indicates that you have a field annotated, but that there’s no getter for this field. Gradle will recognize annotations on fields only if there’s a corresponding getter. If this getter is absent, the annotations have no effect whatsoever.

To fix this, you need to create a getter for this field. We also recommend to annotate the getter instead of the field.

If you are using Groovy, it’s likely that you unintentionally added the private modifier to a property declaration:

@InputFile
RegularFileProperty inputFile // this is a public property

@InputFile
private RegularFileProperty inputFile // this is a field, remove the `private` modifier

Invalid annotation on method

This error indicates that a annotation has been placed on an unexpected method. In general, annotations like @InputFiles or @OutputDirectory need to be placed on a "property" method. A property is defined by having a getter.

This causes Gradle to ignore the annotations, so they are typically not used in up-to-date checking or caching.

To fix this problem, you must remove the annotation, create a getter for the property you want to use and annotate the getter instead.

Mutable type with setter

This error indicates that a property of "mutable" type is also providing a setter. Mutable types in Gradle include Property or ConfigurableFileCollection.

For example, you wrote:

class MyTask extends DefaultTask {
    private Property<Integer> x

    @Input
    Property<Integer> getX() { this.x }

    void setX(Property<Integer> x) { this.x = x }
}

However, mutable types like Property are designed to track dependencies and origin of values they are supplied with. As a consequence, it is an error to make them overwritable, because Gradle will then be unable to tell where a property value comes from.

To fix this problem, you should either make your property final and remove the setter:

class MyTask extends DefaultTask {
    @Input
    final Property<Integer> getX() = ...

You can also rely on Gradle’s built-in capacity to inject final properties:

abstract class MyTask {
    abstract Property<Integer> getX()
}

Then the value of the properties need to be configured via the mutation methods:

myTask {
    x.set(123)
}

Redundant getters

This error indicates that a boolean property has both a get and an is getter method. This is a problem because both getters can be annotated differently and Gradle cannot know which ones to use.

The solution to this problem is to get rid of one of the getters or to mark one of the getters with @Internal

Annotations on private getters

This error indicates that you have annotated a private getter with an input or output annotation. Gradle doesn’t consider private getters as inputs for up-to-date checking, which means that your annotations effectively are ignored. It is important to fix because you might think that you have declared an input when it’s not the case.

To fix this, either make the getter public, or annotate an existing getter instead, or create a new annotated getter.

Annotations on ignored properties

This error indicates that you have a property which is annotated with an annotation which tells Gradle to ignore it (for example @ReplacedBy) but is also annotated with an input annotation (for example @InputFile).

This is an error because Gradle cannot determine if the property should actually be used for up-to-date checking, that is to say if it’s actually an input or not.

To fix this, you must either:

  • remove the input annotations from the property, or

  • remove the ignoring annotation from the property.

Conflicting annotations

This error indicates that a property is annotated with conflicting annotations, that is to say annotations which have different, irreconciliable semantics.

For example, a property cannot be annotated both with @InputFile and @OutputFile at the same time.

To fix this problem, you need to understand the semantics of the different annotations and choose only one.

Annotation is invalid in a particular context

This error indicates that a property was annotated with an annotation which is invalid in a particular context. For example, it’s in general possible to annotate a DirectoryProperty with @OutputDirectory, but this is invalid in the context of an artifact transform, because artifact transforms provide their own workspace.

To fix this problem, you must remove the property.

Properties without annotations

This error indicates that a property isn’t annotated with an input or output annotation. Therefore, Gradle doesn’t know if this property represents an input, an output, or simply should be ignored. As a consequence, up-to-date checking and caching won’t work.

To fix this problem, you need to annotate the property with the appropriate annotation, for example @InputDirectory for a property representing an input directory, or @OutputDirectory for a property representing an output directory.

Alternatively, if the property is internal, that is to say that it shouldn’t participate in up-to-date checking (it’s not an input or an output), then you need to annotate it with @Internal.

Annotation is incompatible with the property type

This error indicates that for a specific kind of property, a modifier annotation doesn’t make sense. This is the case, for example, if the @SkipWhenEmpty is used on an output property. Because there are no semantics associated with this combination, Gradle cannot deduce your intent.

To fix this, you most likely need to remove the conflicting modifier annotation or check that the actual property type is what you intended.

Incorrect use of the @Input annotation

This error indicates that a property is annotated with @Input, but that it should be annotated with @InputFile or @InputDirectory instead.

If you use the @Input annotation on a file-based property, Gradle wouldn’t consider the file contents, or the directory contents, as inputs, as you might expect.

To fix this problem, you need to tell Gradle if the file property represents an input file, in which case you should annotate it with @InputFile, or a directory, in which case it should be annotated with @InputDirectory. If what you really wanted to say is that the actual file path is an input, then you should return a String instead which corresponds to the absolute path of the file.

Unresolvable input

This error indicates that a file collection is used as an input of a task, but that resolution failed, for example because of the use of an invalid notation for a file. Previous versions of Gradle used to skip validation if a task didn’t declare any output but task execution would have failed. In practice, it means that resolving the input file collection would have been a failure at execution time is a failure when inputs are validated.

There are different reasons why the resolution of your file collection have failed. If you actually declared a file collection as an input in order to infer task dependencies, you might want to consider declaring an explicit task dependency instead using Task#dependsOn.

Implicit dependencies between tasks

This error indicates that you have a task which depends on another, but that no explicit or implicit dependency is declared between those two tasks. As a consequence, the results of the build are dependent on the order of execution of tasks, often referred to "accidental dependencies between tasks". Often, this is because you refer directly to the output file of another task instead of using the task directly as an input.

For example, imagine that you have a task which takes a ConfigurableFileCollection as an input and that you have declared a dependency on the jar task using this:

someTask {
    inputFile.from(jar.archivePath)
}

The jar.archivePath property is of type File, which doesn’t carry any task dependency. It means that if you call someTask after jar has been called, the task will succeed, but if the jar is removed, for example, the task would fail.

To fix this, you can declare a Property as an input instead:

someTask {
    inputFile.from(jar.archiveFile)
}

The jar.archiveFile property is of type Provider<RegularFile> which properly carries task dependencies: Gradle will be able to know that the file is generated by the jar task.

It’s actually even easier to add an implicit dependency to the task itself:

someTask {
    inputFile.from(jar)
}

In some cases, for producer tasks which don’t use the configuration avoidance APIs, you can instead declare an explicit dependency on the task:

someTask {
    dependsOn(producer)
    inputFile.from(producer.someFile)
}

In some cases, adding a dependency on the producing task is not desired, for example when the consumer generates reports for possible multiple tasks. In this case you can introduce an ordering between the two tasks by using Task.mustRunAfter().

Input file doesn’t exist

This error occurs whenever a file (or a directory) is declared as an input of a task, but at the moment the task is executed, the file (or directory) doesn’t exist.

Usually, this hints at a missing task dependency: the file should exist before the task is executed, which means that a dependent task wasn’t executed.

The symptoms are similar to Implicit dependencies between tasks except that in this case the task which creates the file hasn’t been executed.

Please refer to the Implicit dependencies between tasks section for possible solutions. If the file isn’t produced by another task, you may want to make sure that it exists before the task is called. If what you want to declare is that it doesn’t matter that the file exists or not when the task is executed, you can configure the input as @Optional and make it return null when the file is absent:

public abstract class SomeTask extends DefaultTask {
    @InputFile
    @Optional
    public abstract RegularFileProperty getSomeOptionalFile();

    @TaskAction
    public void run() {
        RegularFile optionalFile = getSomeOptionalFile().getOrNull();
        if (optionalFile != null) {
           ...
        }
    }
}

Unexpected input file or directory

This error indicates that a property expected a regular file as an input but that it was provided with a directory (or the other way around).

For example, if property is annotated with @InputFile:

@InputFile
File getInputFile()

Then Gradle expects the input file to be a regular file. If the input is a directory, then validation fails.

To fix this problem, you have two options:

  • either you made a mistake an provided a directory instead of a file in which case you just need to fix the input

  • or the task should actually have used a directory as an input, in which case you need to change the type of the property to @InputDirectory

Cannot write to an output file or directory

This error indicates that :

  • an output directory cannot be written because the directory property which has been configured actually refers to a regular file (or something else than an actual directory).

  • or that an output file cannot be written because the file property which has been configured actually refers to a directory.

  • or the parent of the output location exists and is a file.

For example, you’ve set an output directory to /some/path/file.txt instead of /some/path. It’s also possible that you have configured an output directory like /some/path but that an ancestor /some is a regular file.

To fix this problem, make sure that the configured output is a directory (for properties which expect a directory) or a file (for tasks which expect a file).

Cannot write to reserved location

This error indicates that you are trying to write a file to a location which is managed by Gradle only. Typically, this happens whenever you’re trying to write a file directly into an artifact transforms output directory. If you did this intentionally, this is a mistake because those directories should never be written directly: all artifact transform writes should be performed within the artifact transform code itself.

If you didn’t intend to write in this directory, you should simply setup your task to write in a different location.

Unsupported notation in file inputs

This error indicates that a file, directory, collection of files, or a nested collection of files property, refers to an element that Gradle cannot convert to a file.

To fix this, look at the error message which indicates the list of supported file notations and make sure to pick one of them.

Invalid use of @Optional annotation on primitive types

This error indicates that a property of primitive type is also annotated with @Optional. This is similar to null not being assignable to primitive types in Java.

To fix this problem, you have two options:

  • remove the @Optional annotation

  • or use the boxed type (e.g Integer instead of int) if you intend to make the property nullable

Cannot use an input with an unknown implementation

This error indicates that a task uses a class as an input and Gradle cannot track the implementation of the class. Gradle considers the implementation of the following classes as inputs to a task:

  • the task class,

  • the classes of the actions of the task,

  • and the classes of nested inputs of the task, i.e. inputs annotated with @Nested.

There are two reasons why Gradle cannot track the implementation of a class:

  • a Java lambda was used to implement the class,

  • or the class has been loaded by a classloader unknown to Gradle.

Using a Java lambda means that the bytecode uses invoke-dynamic instead of creating an actual sub-class. The class for the invoke-dynamic instruction is generated at JVM runtime and Gradle cannot uniquely identify this class across different JVMs. From Java code, it is quite easy to accidentally use a lambda to implement a task action.

tasks.register("myTask",
    myTask -> myTask.doLast(t -> System.out.println("Hello from task"))
);

Gradle isn’t able to track the implementation of this task action. What you can do instead is to use an anonymous inner class.

tasks.register("myTask",
    myTask -> myTask.doLast(new Action<Task>() {
        @Override
        public void execute(Task task) {
            System.out.println("Hello from task");
        }
    })
);

Using Groovy closures works out of the box since they don’t compile to lambdas in the bytecode. Same is true for Kotlin up to Kotlin 1.4.

Starting with Kotlin 1.5, the Kotlin compiler is using invoke-dynamic in the byte code for SAM wrappers in Kotlin source code. This makes it impossible for Gradle to uniquely identify implementations of SAM wrappers, like for example lambdas for task actions, at runtime. You need to set the languageVersion and apiVersion to 1.4 for the Kotlin compiler to switch back to the previous, supported behavior.

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions {
        apiVersion = "1.4"
        languageVersion = "1.4"
    }
}

For the case where Gradle cannot track the implementation because it was loaded by a classloader unknown to Gradle, you can use Gradle’s built-in ways to load the class.

Missing reason for not caching

This warning indicates that the task or artifact transform action has not been marked as cacheable, though there also is no reason why it is not cacheable. Task or artifact transform authors should always provide a reason why something is not cacheable by using the @DisableCachingByDefault annotation.

To fix the problem either annotate the work type with @CacheableTask/@CacheableTransform or @DisableCachingByDefault(because = "…​").