Skip to content

Adopting NullAway

Vladimir Sitnikov edited this page Sep 5, 2026 · 2 revisions

The first build on an existing codebase reports every dereference, assignment, and field NullAway cannot prove safe, which on a large codebase is a long list. The steps below keep that list manageable, and cover the two migrations that usually come later. The README has the build setup each step assumes.

Turning NullAway on for the first time

  1. Get Error Prone running on its own, and confirm the build is still green. NullAway is a plugin; a problem at this stage is an Error Prone problem. See the Error Prone installation docs, and Configuration for Maven, Bazel, and other build systems.
  2. Tell NullAway which code is annotated. Exactly one of two options must be passed. For a codebase adopting JSpecify, pass -XepOpt:NullAway:OnlyNullMarked=true, which requires NullAway 0.12.3 or later, and mark packages @NullMarked as you go. Otherwise pass -XepOpt:NullAway:AnnotatedPackages with your own package prefixes. Without one of them NullAway refuses to run.
  3. Leave the severity at warning. NullAway reports warnings by default; -Xep:NullAway:ERROR promotes them. Build once and read the count before promoting anything.
  4. Fix or annotate what the first build reports. Most reports are a missing @Nullable on a field, parameter, or return. Error Messages explains each one. Where a value genuinely cannot be null but NullAway cannot prove it, Suppressing Warnings describes castToNonNull.
  5. Suppress the remainder in bulk, if the count is large. See the next section.
  6. Promote to error so that new code cannot add reports, and work the suppressions down from there.

Suppressing an existing codebase's warnings in one pass

For a codebase too large to annotate in one sitting, NullAway emits a suggested fix for each report and Error Prone's patch mode writes a @SuppressWarnings("NullAway") at each one. The check can then be promoted to error immediately, leaving the suppressions as a backlog to work down.

Pass -XepOpt:NullAway:SuggestSuppressions=true together with Error Prone's patch mode:

-XepOpt:NullAway:SuggestSuppressions=true -XepPatchChecks:NullAway -XepPatchLocation:IN_PLACE

Two constraints apply before you run it:

  • Patch mode does not work with NullAway in Error Prone versions after 2.25.0 and before 2.37.0; see Compatibility.
  • -XepOpt:NullAway:AutoFixSuppressionComment writes a comment beside each inserted suppression, so a later reader can tell this batch from a deliberate suppression. Its entry has the constraints on the value.

Suppressing Warnings covers the suppression mechanisms themselves, including castToNonNull, which narrows a suppression to one expression.

Moving from AnnotatedPackages to OnlyNullMarked

This migration replaces the AnnotatedPackages prefixes with @NullMarked annotations in the source, in three steps, and requires NullAway 0.12.3 or later. JSpecify tools and other nullness checkers read the annotations; they do not read the prefixes.

  1. Add @NullMarked to every package or class that the AnnotatedPackages prefixes covered. A package-info.java per package is the usual form. AnnotatedPackages is still the configured setting at this point, and @NullMarked code is treated as annotated whether or not a prefix covers it, so this step changes nothing on its own.
  2. Turn on the RequireExplicitNullMarking checker at warning level, with -Xep:RequireExplicitNullMarking:WARN. It reports every top-level class that is not itself @NullMarked or @NullUnmarked and whose package and module are not either. The check reads no AnnotatedPackages setting, so its report also names classes the prefixes never covered; the ones inside the prefixes are what step 1 missed.
  3. Replace AnnotatedPackages with -XepOpt:NullAway:OnlyNullMarked=true. The two cannot both be passed. Requires NullAway 0.12.3 or later.

A class that ends up neither @NullMarked nor covered by a prefix is not checked, and nothing reports it. Step 2 finds those classes before step 3 removes the prefixes.

Moving to JSpecify mode

JSpecify mode adds checking of nullability inside generic types and arrays. It is a larger step than the previous two, because it reports errors that no earlier configuration could report.

  1. Check the javac requirement first: JDK 22 or later, or -XDaddTypeAnnotationsToSymbol=true. NullAway fails the build rather than checking silently; see Supported JDK versions.
  2. Turn on -XepOpt:NullAway:JSpecifyMode=true at warning level and read the count. Expect new reports, especially at calls into Guava and into generic APIs; Error Messages covers the JSpecify-mode errors, and JSpecify and Guava covers what an upgraded Guava adds.
  3. Annotate type arguments where the reports call for it, using @Nullable in type-use position.
  4. Add -XepOpt:NullAway:JSpecifyExperimental=true once step 3 stops producing new reports. It turns on the JDK nullness models, wildcard support, and warnings when inference fails for a generic method call. NullAway expects to enable it by default in a future release.

Clone this wiki locally