Open-source project
uber/NullAway avatar
uber/NullAway

NullAway: you annotate first, it checks the dereferences second

A tool to help eliminate NullPointerExceptions (NPEs) in your Java code with low build-time overhead.

4,134 stars381 forksJavaMIT

At a glance

What is it?
An Error Prone plugin that turns nullable annotations into compile errors, with the honest admission that it does not catch every null dereference. The build setup has two mandatory knobs, one of which you cannot omit, and code generators can make it fail the build.
Who is it for?
Take NullAway if your Java codebase is large enough that null dereferences matter and small enough that annotating it is a day's work rather than a quarter's. Check three things before you start.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • uber/NullAway4,134 ★ · +52
Star history
Repository2026-07-282026-11-13
uber/NullAway4.1k4.1k

52 Stars in the last 108 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Annotations first, then type-based local checks on dereferences

The workflow is two steps and the order matters. You add a nullable annotation wherever a field, a method parameter, or a return value may be null, and only then does the checker do anything. Given those annotations, it performs a series of type-based local checks to ensure that any pointer dereferenced in your code cannot be null. The word local is doing real work in that sentence: the analysis is not an interprocedural proof across your whole program, which is exactly why it runs on every build without costing much. The project positions it against two things explicitly. It is similar to the type-based nullability checking built into Kotlin and Swift, so if you have written against those you will recognise the model. And it sits alongside two other Java null checkers, the Checker Framework and Eradicate, which is the comparison to read first if you are choosing a checker rather than writing one.

Under ten percent overhead, by the project's own measurement

The performance claim is specific and modest, and it comes with its conditions visible. The tool is built as a plugin to Error Prone, which means it rides on a compiler plugin you probably already have in your build rather than adding a new analysis stage, and the project states that in their measurements the build-time overhead of running it is usually less than ten percent. The word usually is the honest part: this is a typical figure from their own runs, not a bound. The more interesting claim is the second one, about practicality. The project says plainly that the tool does not prevent all possible null dereferences, but catches most of the ones observed in production while imposing a reasonable annotation burden, which is the trade it is actually selling: you accept incomplete coverage in exchange for a cheap check that runs on every build. If your failure mode is a rare race rather than a plain forgotten null check, that distinction is the one to hold on to.

One of two options is mandatory, and there is no default

This is the configuration detail that costs people an afternoon. The checker requires exactly one of two options to be supplied in order to distinguish annotated code from unannotated code: a list of packages considered annotated, or a switch to only look at code marked with the standard nullness marker. There is no sensible default, and the reason is structural rather than cautious. Without either option the checker cannot tell whether a package is one you have annotated or one you have not, and that distinction is what keeps it from reporting every existing null dereference in a codebase on the first run. In a Gradle build the shape is a plugin for Error Prone integration plus three dependencies on the Error Prone configuration, one of which supplies the annotation library:

gradle
plugins {
  // we assume you are already using the Java plugin
  id "net.ltgt.errorprone" version "<plugin version>"
}

dependencies {
  errorprone "com.uber.nullaway:nullaway:<NullAway version>"

  // Some source of nullability annotations; JSpecify recommended,
  // but others supported as well.
  api "org.jspecify:jspecify:1.0.0"
}

Severity is the other knob and it defaults to warnings. Passing a check configuration at the error level is what turns findings into build failures, which is the setting the project recommends and the one that makes the tool a gate rather than a report.

Generated code from Dagger and AutoValue will block the build

There is a section on annotation processors and generated code, and it exists because a strict severity setting turns an ordinary build tool into a build failure. Some processors generate code into the same package namespace as your own code, so with findings escalated to errors the errors in that generated code block the build, even though you did not write it. The recommended fix is to disable the Error Prone checks on generated code using the excluded-paths option, which was added in a specific Error Prone version, with the Gradle equivalent named on the page. You have to work out which directory holds the generated output and add it to the exclusion pattern, which is the fiddly part. The page also carries a specific note for one widely used processor: versions older than a particular release interact badly with the checker, and updating is the fix. That is a compatibility statement about a specific dependency version, and it is the kind of detail that gets fixed silently and then surprises you after an upgrade.

Android needs an older plugin major version, or extra configuration

The mobile story is a constraint rather than a supported path, and the page is explicit about why. From version three onwards, the Gradle plugin that integrates Error Prone no longer supports Android at all, so a recent version of that plugin requires additional configuration to run either Error Prone or the null checker in an Android project. The project points at a sample application's build file as one way to do it and warns that your project may need tweaks. The alternative it offers is to stay on the second major version of that plugin, which still supports Android and may still work. That is a genuine tradeoff rather than a workaround: staying behind on the integration plugin to keep mobile building is a real cost in a build you control, and the project's framing leaves the decision open. One convenience is specific to Android, where the separate annotation library dependency can be dropped entirely in favour of the annotation from the platform's own support library.

The tree is a laboratory, not just a checker

The repository layout explains what it takes to maintain something that sits inside other people's compilers. There is the checker itself, a bill of materials directory for it, and a separate annotations directory, which together are the parts a consumer actually depends on. Then there is a jar inference directory, which is the part that makes the tool useful on code you have not annotated: it reads bytecode to work out which parameters are already nullable and marked non-null in the class files of a dependency. Beside it sit directories for JDK annotations and for a compiler plugin, and several test directories named for the specific compatibility problems the project tracks, one for a Java library, one for a module, one for Lombok, and two named for recent JDK and Guava versions, which is a good signal of where nullness changes tend to break a checker. A benchmarking directory suggests performance claims are measured rather than asserted, and the sample applications exist so the build configuration in the documentation is exercised rather than hypothetical.

Patch releases roughly weekly, and an annotated-agent workflow

The release record is three patch versions of the same minor line in about five weeks, which is what a tool that has to track new compiler releases looks like. The newest is from late September and the previous two are within the same month, and the branch moved on the day before this page was read, so the project is not sitting still. The tree also contains a file for repository-specific instructions aimed at coding agents and a changelog and a release process document, plus a configuration for an automated dependency update tool and one for a review bot. That combination is a fairly recent addition to a codebase of this age, and it says something about maintenance practice: the human-facing process is documented, and the mechanical parts are being taken off people. For an adopter the practical effect is that a checker tied to compiler internals is being kept aligned with those internals, which is the main way this kind of tool normally rots.

Editorial conclusion

Take NullAway if your Java codebase is large enough that null dereferences matter and small enough that annotating it is a day's work rather than a quarter's. Check three things before you start. You must supply exactly one of the two configuration options that tell it which code is annotated, so there is no zero-configuration mode to begin with. Code generators that emit into your own package namespace will produce findings in generated code and block the build at the strict severity unless you exclude the generated paths. And the tool is not a proof, since the project says outright that it does not prevent every possible null dereference, so keep a runtime null-safety story in place rather than treating a clean build as a guarantee.

Frequently asked questions

how to use nullaway

Annotate every field, parameter, and return that may be null, then add the Error Prone plugin to your build along with the checker and an annotation library, and supply exactly one of the annotated-packages or null-marked options. Findings default to warnings, so set the check to the error level to make them fail the build.

nullaway vs checker framework

The project names the Checker Framework as one of the two other Java null checkers, alongside Eradicate, and describes itself as similar to the type-based nullability checking in Kotlin and Swift. The distinguishing claim is cost: local type-based checks as a compiler plugin, under ten percent build overhead in the project's measurements, rather than a whole-program analysis.

Does NullAway catch every NullPointerException?

No, and the project says so directly: it does not prevent all possible null dereferences but catches most of the ones observed in production while imposing a reasonable annotation burden. It performs local type-based checks on dereferences rather than a whole-program proof.

What does NullAway need to run?

A build on JDK 17 or higher and Error Prone at 2.36.0 or higher. The instructions assume Gradle, and other build systems are covered in the configuration documentation on the wiki. With the JSpecify mode enabled the project recommends the most recent JDK available.

Why do generated code files break my NullAway build?

Some annotation processors generate code into the same package namespace as your own, so at the error severity those findings block the build. The recommended fix is to exclude the generated code paths from the checks, using the option added in a particular Error Prone release, after working out which directory holds the output.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/uber-nullaway.svg)](https://hysenlabs.com/projects/uber-nullaway)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/uber-nullaway.svg)](https://hysenlabs.com/projects/uber-nullaway)

Was this useful?