-
Notifications
You must be signed in to change notification settings - Fork 366
Error Messages
Here we explain the different warning messages that NullAway produces and how to address them. For an overview of NullAway see the main README.
- dereferenced expression is @Nullable
- returning @Nullable expression from method with @NonNull return type
- passing @Nullable parameter where @NonNull is required
- assigning @Nullable expression to @NonNull field
- method returns @Nullable, but superclass method returns @NonNull
- referenced method returns @Nullable, but functional interface method returns @NonNull
- parameter is @NonNull, but parameter in superclass method is @Nullable
- parameter is @NonNull, but parameter in functional interface method is @Nullable
- unbound instance method reference cannot be used, as first parameter of functional interface method is @Nullable
- initializer method does not guarantee @NonNull field is initialized / @NonNull field not initialized
- read of @NonNull field before initialization
- unboxing of a @Nullable value
- Method is annotated with @EnsuresNonNull but fails to ensure the following fields are non-null at exit
- Expected field to be non-null at call site due to @RequiresNonNull annotation on invoked method
- empty @RequiresNonNull/@EnsuresNonNull is the default precondition for every method, please remove it
- currently @RequiresNonNull/@EnsuresNonNull supports only class fields of the method receiver: fieldName is not supported
- for @RequiresNonNull/@EnsuresNonNull annotation, cannot find instance field in class
- postcondition inheritance is violated, this method must guarantee that all fields written in the @EnsuresNonNull annotation of overridden method are @NonNull at exit point as well. Fields must explicitly appear as parameters at this method @EnsuresNonNull annotation
- precondition inheritance is violated, method in child class cannot have a stricter precondition than its closest overridden method, adding @RequiresNonNull for fields makes this method precondition stricter
- Method is annotated with @EnsuresNonNullIf but does not ensure fields: fieldNames
- Method is annotated with @EnsuresNonNullIf but does not return boolean
- switch selector expression is @Nullable
- Invoking get() on possibly empty Optional
- passing known @NonNull parameter to CastToNonNullMethod
- Type-use nullability annotations should be applied on inner class
- incompatible types: cannot be converted
- Conditional expression has mismatched nullability of type parameters
- mismatched type parameter nullability when overriding a method
- Type argument cannot be @Nullable
- Generic type parameter cannot be @Nullable
- inference failure: type variable
- illegal location for annotation: nullness annotations cannot be written directly on a wildcard
- Writing @Nullable expression into array with @NonNull contents
This error occurs when code reads a field, writes a field, or invokes a method on some expression, and that expression might be null. Example:
Object x = null;
x.toString(); // dereferencing x, which is nullTo fix this error, you can move the dereference under a check that the expression is not null. E.g.:
Object x = null;
if (x != null) {
x.toString(); // this is safe now
}Be sure to add appropriate logic to handle the case where the expression is null! Or, even better, try to find a way to rewrite the code so the expression can't be null in the first place.
This error occurs when code is returning a @Nullable expression from a method whose return type is not annotated as @Nullable. Example:
Object m(@Nullable Object x) {
return x; // oops! return type is unannotated, hence @NonNull
}To fix this error, you can add a @Nullable annotation to the return type, e.g.:
@Nullable
Object m(@Nullable Object x) {
return x;
}Or, you can place the return statement under an appropriate null check:
Object m(@Nullable Object x) {
if (x != null) {
return x;
} else {
return new Object();
}
// this also works: return (x == null) ? new Object() : x;
}This can happen in cases like the following:
void nonNullParam(Object x) {}
void caller(@Nullable Object y) {
nonNullParam(y); // bad!
}To fix, either:
- Make sure
yis non-null at the pointnonNullParam(y)is called (e.g. by making the parameter tocalleritself@NonNull, or adding an appropriate null check beforenonNullParam(y)is called), or - Make the parameter
@NullableinnonNullParam's definition.
Here's an example of how this can happen:
class Foo {
Object myField; // @NonNull since it's not annotated
void writeToField(@Nullable Object z) {
this.myField = z; // bad!
}
}To fix, either make the right-hand side of the assignment @NonNull, place the assignment under an appropriate null check for the right-hand side, or make the field @Nullable.
This error means you have overridden a superclass method in an invalid way. Here's an example of why it's bad.
class Super {
Object getObj() { return new Object(); }
}
class Sub extends Super {
@Override
@Nullable
getObj() { return null; }
}
class Main {
void caller() {
Super x = new Sub();
x.getObj().toString(); // NullPointerException!
}
}The key idea is that when code gets an object of type Super, it needs to be able to rely on the fact that Super.getObj() returns a @NonNull value. If subclassing breaks this guarantee, it can lead to NullPointerExceptions.
This is a version of the previous error specific to method references. Example:
interface NoArgFunc {
Object apply();
}
class Test {
static Object doApply(NoArgFunc f) {
return f.apply();
}
@Nullable
static Object returnNull() { return null; }
static void test() {
doApply(Test::returnNull).toString(); // NullPointerException!
}
}The key idea is that when code invokes NoArgFunc.apply(), it needs to be able to rely on the fact that the return value will be @NonNull. If a method reference breaks this guarantee, it can lead to NullPointerExceptions.
This error is similar to the previous one regarding bad overriding and return types. Here's an example of why it's needed:
class Super {
void handleObj(@Nullable Object obj) { }
}
class Sub extends Super {
@Override
void handleObj(@NonNull Object obj) {
obj.toString();
}
}
class Main {
void caller() {
Super x = new Sub();
x.handleObj(null); // this will cause a NullPointerException
}
}Subclasses cannot safely override a method and annotate a parameter as @NonNull if the overridden method has a @Nullable parameter.
This is just like the previous error, but specific to lambdas and method references. Example:
interface NullableArgFunc {
void apply(@Nullable Object o);
}
class Test {
static void doApply(NullableArgFunc f, @Nullable Object p) {
f.apply(p);
}
static void derefArg(Object q) {
q.toString();
}
static void test() {
doApply((Object x) -> { print(x.hashCode()); }, null); // NullPointerException
doApply(Test::derefArg, null); // also NullPointerException
}
}Lambdas / method references cannot assume a parameter is @NonNull if the functional interface declares it as @Nullable.
Note that if the first doApply call were written as doApply(x -> { print(x.hashCode()); }, without the explicit type for the parameter, NullAway would treat the parameter as @Nullable (inherited from the functional interface method), and hence would report an error at the dereference x.hashCode().
unbound instance method reference cannot be used, as first parameter of functional interface method is @Nullable
This is the same as the previous error, specialized to the case of the first parameter of an unbound instance method reference (or a "Reference to an instance method of a arbitrary object supplied later"; see here). Example:
interface NullableArgFunc<T> {
void apply(@Nullable T o);
}
class Test {
static <T> void doApply(NullableArgFunc<T> f, @Nullable T p) {
f.apply(p);
}
void instMethod() {}
static void test() {
doApply(Test::instMethod, null); // NullPointerException
}
}Here, writing Test::instMethod is equivalent to writing x -> { x.instMethod(); }. Hence, the doApply call will cause an NPE.
initializer method does not guarantee @NonNull field is initialized / @NonNull field not initialized
This error indicates that a @NonNull field may not be properly initialized in some case. There are two ways to initialize a @NonNull field: (1) in the constructors of a class, or (2) in a designated initializer method. For the case of constructors, each @NonNull field must be initialized in every constructor (a constructor that invokes another constructor via this(...) is excluded). A field can also be initialized in a method called by the constructor, provided that the method is either private or final (to prevent overriding) and the constructor invokes the method at the top-level, not under an if condition (to guarantee the call always executes). NullAway only detects cases where a field is initialized via a direct call to another method from a constructor, not via a chain of calls.
An example:
class C1 {
// non-null fields
Object f1, f2;
// good constructor
C1(Object f1) {
this.f1 = f1;
// initializing f2 in doInit() is ok, since it's a
// private method and called at the top-level
doInit();
}
private void doInit() {
this.f2 = new Object();
}
// bad constructor
C1(boolean flag) {
if (flag) {
// bad: method called under a condition
doInit();
}
// missing initialization of f1
}
} Initialization can also occur in special initializer method to handle alternate initialization protocols, like Android lifecycles. The checker has built-in knowledge of the following initialization methods:
'android.view.View.onFinishInflate',
'android.app.Service.onCreate',
'android.app.Activity.onCreate',
'android.app.Fragment.onCreate',
'android.app.Application.onCreate',
'javax.annotation.processing.Processor.init'
Overrides of these methods will be treated as initializers. (If you think any third-party library methods should be added to the list, please file an issue.) You can specify a method as an initializer with any @Initializer annotation, but this should be used very sparingly; it is best not to introduce one-off complex initialization patterns. As with constructors, initializers can invoke private or final methods at the top level to perform initialization.
Note that for a field to be considered initializer by an initializer method, a non-null value must be assigned to that field over all possible paths through that method. The following code would not be considered to have initialized foo, as there is at least one case (due to the early return) where foo was not initialized.
class C2 {
// non-null fields
Object foo;
C2() { }
@Initializer
public int initMe() {
if (!sanityCheck()) {
return -1; // Error code.
}
this.foo = new Object();
return 0
}
}If you get a field initialization error, you can fix it by ensuring the field is initialized in all cases, or by making the field @Nullable.
Besides any annotation with the @Initializer simple name, NullAway recognizes a built-in list of equivalent initializer annotations covering @PostConstruct, JUnit, and Spring, plus any fully-qualified annotation name passed to -XepOpt:NullAway:CustomInitializerAnnotations, whose entry lists the built-in ones.
This error is reported when a @NonNull field is used before it is
initialized, e.g.:
class C {
Object foo;
C() {
this.foo.toString(); // foo not initialized yet!
this.foo = new Object();
}
}To fix this error, perform the initialization before reading the field.
This error is reported when some @Nullable expression gets implicitly unboxed by some operation. For example:
Integer i1 = null;
int i2 = i1 + 3; // NullPointerExceptionThese errors can be fixed in the same manner as the dereferenced expression is @Nullable error.
Method is annotated with @EnsuresNonNull but fails to ensure the following fields are non-null at exit
This error is reported when a method is annotated with @EnsuresNonNull and a field mentioned in the annotation's parameters is not guaranteed to be @NonNull at all exit points.
Please see the example below where this error will be reported.
class C {
Object foo;
@EnsuresNonNull("foo")
C(boolean b) {
if(b){
foo = new Object();
}
// Here foo may still be null
}
}To fix this error, either make sure foo is initialized along all paths of the constructor at the exit point or remove the @EnsuresNonNull annotation.
This error indicates that the called method is annotated with @RequiresNonNull and expects a certain class fields given in the @RequiresNonNull parameter to be @NonNull before the call.
Please see the example below where this error will be reported.
class C {
@Nullable Object foo;
//this methods ensures class field "foo" to be non-null at exit point.
@EnsuresNonNull("foo")
void ensuresNonNullFoo() {
foo = new Object();
}
//this methods requires class field "foo" to be non-null at call site.
@RequiresNonNull("foo")
String requiresFoo(){
return foo.toString();
}
//class field "foo" is non-null at call site of "requiresFoo", hence no error will be reported.
void test1(){
foo = new Object();
requiresFoo()
}
//class field "foo" is non-null via call to "ensuresNonNullFoo" at call site of "requiresFoo", hence no error will be reported.
void test2(){
ensuresNonNullFoo()
requiresFoo()
}
//class field "foo" is potentially null at call site of "requiresFoo", hence the above error will be reported.
void test3(){
requiresFoo()
}
}To fix this error, make sure the reported field is guaranteed to be non-null before the call.
empty @RequiresNonNull/@EnsuresNonNull is the default precondition for every method, please remove it
Empty @RequiresNonNull and @EnsuresNonNull is the default type for all methods. Therefore we report an error on such cases to keep the code simple.
Please see the example below where this error will be reported.
class C {
Object foo;
@EnsuresNonNull() //Here the following error will be reported since no class field is given in parameters
void ensures(){
}
@RequiresNonNull() //Here the following error will be reported since no class field is given in parameters
void requires(){
}
}To fix this error, smiply remove the empty @RequiresNonNull or @EnsuresNonNull annotation.
currently @RequiresNonNull/@EnsuresNonNull supports only class fields of the method receiver: fieldName is not supported
This error indicates that the annotation parameter is violating the following syntax rule:
The annotation may only refer to fields of the receiver object.
Please see the example below where this error will be reported.
class C {
Object foo;
C other;
@EnsuresNonNull("other.foo") // error: can only be either "foo" or "this.foo"
void ensures(){
}
@RequiresNonNull("other.foo") // error: can only be either "foo" or "this.foo"
void requires(){
}
}While we are considering supporting such syntax in the future, right now NullAway isn't able to gain any information from such annotation. To reduce developer confusion we choose to report an error.
This error indicates that the annotation parameter is violating the following syntax rule:
All parameters given in the annotation must be one of the fields of the containing class or its super classes.
Please see the example below where this error will be reported.
class Super {
Object bar;
}
class C extends Super{
Object foo;
@EnsuresNonNull("foo") // no error: field is present in "C"
void ensures(){
foo = new Object();
}
@RequiresNonNull("bar") // no error: field is present in super class "Super"
void requiresBar(){
//not important
}
@RequiresNonNull("tmp") // error: field is not present in this class or any of it's super classes.
void requiresTmp(){
//not important
}
}postcondition inheritance is violated, this method must guarantee that all fields written in the @EnsuresNonNull annotation of overridden method are @NonNull at exit point as well. Fields must explicitly appear as parameters at this method @EnsuresNonNull annotation
This error indicates that the annotation violates the following inheritance rule:
Every method must satisfy all postcondition of its super method.
Without this rule, a postcondition of @EnsuresNonNull("field") void foo(...) on type Supertype might be violated for o.foo(...) on Supertype o = new Subtype(),
depending on whether and how Subtype overrides the method foo of Supertype.
Please see the example below that demonstrates the necessity of this rule.
class SuperType{
Object foo;
@EnsuresNonNull("foo")
void ensures(){
foo = new Object();
}
@RequiresNonNull("foo")
String requires(){
return o.toString();
}
}
class Subtype extends Super{
void ensures(){ }
void error(){
SuperType o = new Subtype();
o.ensures();
o.requires(); // call to requires potentialy leads to null pointer exception.
}
}Please see link for more information regarding inheritance rules.
All overriding methods can only add new field names to the set of fields of its super method
mentioned in EnsuresNonNull and all should be mentioned explicitly in the annotation parameters.
Please see the example below where this error will be reported.
class Super {
Object bar;
@EnsuresNonNull("bar")
void ensures(){
foo = new Object();
}
}
class Child extends Super{
Object foo;
@EnsuresNonNull("foo") // error: method does not satisfy all its super methods post condition. "bar" must be also mentioned explicitly in the annotation parameter.
void ensures(){
foo = new Object();
}
}To fix this error, simply add bar as a parameter to @EnsuresNonNull annotation on Child.ensures method.
precondition inheritance is violated, method in child class cannot have a stricter precondition than its closest overridden method, adding @requiresNonNull for fields makes this method precondition stricter
This error indicates that the annotation violating the following inheritance rule:
Every method cannot have a stricter precondition than its super method
Without this rule, a precondition of @RequiresNonNull("field") void foo(...) on type Supertype might be violated for o.foo(...) on Supertype o = new Subtype(),
depending on whether and how Subtype overrides the method foo of Supertype.
Please see the example below that demonstrates the necessity of this rule.
class SuperType{
@Nullable Object nullableFoo;
@Nullable Object nullableBar;
@RequiresNonNull("foo")
String requires(){
nullableBar = new Object();
return nullableBar.toString() + nullableFoo.toString();
}
}
class Subtype extends Super{
@RequiresNonNull("foo", "bar")
String requires(){
return nullableBar.toString() + nullableFoo.toString();
}
void error(){
SuperType o = new Subtype();
o.nullableFoo = new Object();
o.requires(); // call to requires potentialy leads to null pointer exception.
}
}Please see link for more information regarding inheritance rules.
No overriding methods can add new field names to the set of fields of its super method mentioned in RequiresNonNull annotation.
Please see the example below where this error will be reported.
class Super {
Object bar;
@RequiresNonNull("bar")
String Requires(){
return bar.toString();
}
}
class C extends Super{
Object foo;
@RequiresNonNull({"foo", "bar"}) // error: Having the preconidton of "foo" in addition to "bar" to be non-null at call site makes this method precondition stricter than it's super method.
String Requires(){
return bar.toString() + foo.toString();
}
// no-error: this method does not have a stricter precondition than its super method. It still requires "bar" to be non-null at call site.
void Requires(){
}
}To fix this error, remove foo as a parameter to @RequiresNonNull annotation on the C.Requires method.
This error happens when the method annotated with @EnsuresNonNullIf doesn't ensure the non-nullability of the passed fields. For example:
class Foo {
@Nullable private Object item;
@EnsuresNonNullIf("item")
public boolean hasItem() {
// Error here
// This is wrongly implemented, as the method returns true but item is nullable
return item == null;
}
public void operation() {
if(!hasItem()) {
return;
}
// from here on, item is assumed to be non-null
}
}This error happens when the method annotated with @EnsuresNonNullIf doesn't return boolean. For example:
class Foo {
@Nullable private Object item;
@EnsuresNonNullIf("item")
public void hasItem() {
// ERROR: this method should return boolean
}
}This error is reported when the selector of a switch statement or expression may be null and there is no case null label. A switch dereferences its selector, so a boxed selector is unboxed and a String or enum selector has a method invoked on it. Example:
void foo(@Nullable Integer s) {
// BUG: switch selector expression 's' is @Nullable
switch (s) {
case 5: break;
}
}To fix this error, check the selector for null before the switch, or, on Java 21 and later, add a case null label.
This error is reported when get() is invoked on an Optional that NullAway cannot show is non-empty. It is reported only under -XepOpt:NullAway:CheckOptionalEmptiness=true, which is off by default. Example:
Optional<Object> a = Optional.empty();
// BUG: Invoking get() on possibly empty Optional a
a.get().toString();To fix this error, guard the call with isPresent(), or use orElse, orElseThrow, or ifPresent instead. @SuppressWarnings("NullAway.Optional") suppresses this check alone.
This error is reported when a cast-to-non-null method is invoked with an argument that NullAway already considers @NonNull, which makes the cast dead code. The method is the one named by -XepOpt:NullAway:CastToNonNullMethod, or one a library model registers, which is why the message names an argument position. The full message names the method and that position:
passing known @NonNull parameter 'x' to CastToNonNullMethod (com.example.Util.castToNonNull) at position 0. This method argument should only take values that NullAway considers @Nullable at the invocation site, but which are known not to be null at runtime.
To fix this error, drop the call and pass the expression directly; under SuggestSuppressions the suggested fix removes it. NullAway does not report the call inside a constructor, an initializer method, or an initializer block. See Suppressing Warnings for when to use castToNonNull.
This error is reported when a type-use @Nullable annotation on a qualified type lands on an enclosing type rather than on the innermost one. @Nullable A.B.C annotates A, not C. Example:
import org.jspecify.annotations.Nullable;
class A { class B { class C {} } }
class Test {
// BUG: Type-use nullability annotations should be applied on inner class
@Nullable A.B.C foo1 = null;
// BUG: Type-use nullability annotations should be applied on inner class
A.@Nullable B.C foo2 = null;
A.B.@Nullable C foo3 = null; // correct
}Which annotation you import decides this. An annotation that is also a declaration annotation, such as javax.annotation.Nullable, is accepted at the outermost position and read as a declaration annotation, so foo1 above would not be reported.
To fix this error, move the annotation to the innermost type. See Type-Use Annotation Placement for the placement rules on qualified and array types.
This error is reported in JSpecify mode when the nullability of a type argument or of an array component differs between the two sides of an assignment, an argument being passed, or a return. Java's own subtyping accepts the conversion; NullAway rejects it because the nullability of the type parameters does not match. The message names both types:
incompatible types: Foo<@Nullable String> cannot be converted to Foo<String>
incompatible types: @Nullable Object [] cannot be converted to Object []
Example:
Foo<@Nullable String> nullableFoo = ...;
// BUG: incompatible types: Foo<@Nullable String> cannot be converted to Foo<String>
Foo<String> foo = nullableFoo;Nullability of type arguments is invariant, exactly as Java's own generics are invariant in their type arguments: Foo<@Nullable String> is not a subtype of Foo<String> and not a supertype of it either. To fix this error, make the two sides agree, or use a wildcard where the API should accept both.
This error is reported in JSpecify mode when a branch of a ? : expression has type arguments or array components whose nullability does not fit the type the whole expression must have. That type comes from the context the expression sits in, such as the variable it is assigned to or the parameter it is passed as, so branches that differ from each other are fine as long as each one fits:
Conditional expression must have type Integer [] but the sub-expression has type @Nullable Integer [], which has mismatched nullability of type parameters
Example:
// BUG: Conditional expression must have type Integer []
Integer[] t1 = t ? new Integer[0] : new @Nullable Integer[0];
// legal: both branches fit the target type
@Nullable Integer[] t2 = t ? new Integer[0] : new @Nullable Integer[0];To fix this error, give the offending branch the nullability the target requires, or annotate the target to accept what the branches produce.
This error is reported in JSpecify mode when an override, or a method reference bound to a functional interface, differs from the method it implements in the nullability of a type argument or an array component. There are four forms: the return type and a parameter type, each for a declared override and for a method reference:
Method returns @Nullable Integer [], but overridden method returns Integer [], which has mismatched type parameter nullability
Parameter has type String [], but overridden method has parameter type @Nullable String [], which has mismatched type parameter nullability
referenced method returns Test.Foo<@Nullable String>, but functional interface method returns Test.Foo<String>, which has mismatched type parameter nullability
parameter type of referenced method is Test.Foo<@Nullable String>, but parameter in functional interface method has type Test.Foo<String>, which has mismatched type parameter nullability
Example:
interface Foo<T extends @Nullable Object> {
void accept(T[] values);
}
Foo<@Nullable String> f = new Foo<@Nullable String>() {
@Override
// BUG: Parameter has type String [], but overridden method has parameter type @Nullable String []
public void accept(String[] values) {}
};To fix this error, write the type arguments of the overriding method exactly as the overridden method writes them. Unlike the top-level nullability of a parameter or a return, these are invariant, so neither widening nor narrowing them is allowed.
This error is reported in JSpecify mode when an explicit type argument at a generic method call is @Nullable, but the method's type variable has no @Nullable upper bound:
Type argument cannot be @Nullable, as method <T>nonNullIdentity(T)'s type variable T is not @Nullable
Example:
static <T> T nonNullIdentity(T t) { return t; }
// BUG: Type argument cannot be @Nullable
Test.<@Nullable Object>nonNullIdentity(new Object());To fix this error, either drop the @Nullable from the type argument, or declare the type variable as <T extends @Nullable Object> if the method really does accept null.
This error is reported in JSpecify mode when a type argument written on a generic type is @Nullable, but the class's type variable has no @Nullable upper bound. The same rule applied to an explicit type argument at a generic method call is reported as Type argument cannot be @Nullable.
Generic type parameter cannot be @Nullable, as type variable T of type Baz does not have a @Nullable upper bound
Example:
class Baz<T, U> {}
// BUG: Generic type parameter cannot be @Nullable, as type variable T of type Baz does not have a @Nullable upper bound
Baz<@Nullable String, Object> baz = ...;To fix this error, either drop the @Nullable, or declare the class as class Baz<T extends @Nullable Object, U>.
This error is reported in JSpecify mode when NullAway cannot infer a consistent nullability for a type variable at a generic method call. It is reported only under -XepOpt:NullAway:WarnOnGenericInferenceFailure=true, which JSpecifyExperimental turns on. There are two forms:
inference failure: type variable T is constrained to be @Nullable, but its upper bound requires it to be @NonNull
inference failure: type variable R constrained to be both @NonNull and @Nullable
Example:
<T> T call(Supplier<T> s) { return s.get(); }
// BUG: inference failure: type variable T is constrained to be @Nullable, but its upper bound requires it to be @NonNull
call(() -> { return null; });The first form usually means the call really does need a @Nullable type argument that the declaration does not allow; declare the type variable as <T extends @Nullable Object>, or stop passing null. The second form can also come from a gap in NullAway's inference; where the code looks correct, pass the type argument explicitly and report an issue. See JSpecify Support for what inference does not yet cover.
This error is reported when a nullness annotation is written on a wildcard itself rather than on its bound. JSpecify gives an annotation in that position no meaning. It is reported only in JSpecify mode and only under -XepOpt:NullAway:HandleWildcardGenerics=true, which JSpecifyExperimental turns on:
illegal location for annotation: nullness annotations cannot be written directly on a wildcard; annotate the wildcard bound instead
Example:
// BUG: illegal location for annotation
Function<@Nullable ? super K, ? extends V> f1;
// correct
Function<? super @Nullable K, ? extends V> f2;To fix this error, move the annotation after the super or extends keyword. An unbounded wildcard has no such keyword, so write ? extends @Nullable Object or drop the annotation.
The JSpecifyUnrecognizedAnnotationLocation checker, which is off by default and independent of JSpecify mode, reports this location and every other location JSpecify does not recognize. It reports the wildcard case with its own text, A nullness annotation directly on a wildcard has no meaning under JSpecify., and carries a fix that moves the annotation to the bound.
This error is reported in JSpecify mode when a @Nullable value is stored into an array whose component type is not @Nullable:
Writing @Nullable expression into array with @NonNull contents.
Example:
Object[] arr = new Object[10];
// BUG: Writing @Nullable expression into array with @NonNull contents.
arr[0] = null;To fix this error, either check the value for null before the write, or declare the array with a @Nullable component type: @Nullable Object[] arr. Note that @Nullable Object[] is an array of nullable elements, while Object @Nullable [] is a nullable array; see Type-Use Annotation Placement.
Outside JSpecify mode NullAway does not track the nullability of array contents at all; see Deliberate Unsoundness.
Getting NullAway running
Writing the annotations
When NullAway reports something
Internals