Transitive dependency on new, problematic Guava version
Hi! Our organization is large: we have a few thousand Maven projects plus a Bazel repo, all importing an internal BOM which in turn imports Google's libraries-bom. Many libraries of course depend on error-prone-annotations, and we also have a few internal checks that import -core.
The latest error-prone 2.38.0 upgraded to Guava 33.4.6, part of a series of releases that introduce a few problems. In practice, the changes with modularization and nullability checks means that many projects don't build with the upgrade and require some time to fix.
We had a very similar situation a few months ago where error-prone upgraded protobuf to a release that is known to introduce complicated breaking changes that can take a long time to fix.
Since in Maven, transitive dependencies are intended as minimum requirements, and in fact to avoid problems we run the enforcer-plugin with the requireUpperBounds check, when a widely used tool like error-prone upgrades to very recent and problematic releases of libraries like protobuf and guava, large organizations especially can have problems.
Specifically for Guava in this case, was this upgrade really necessary or could it be reverted? Maybe error-prone could have a policy to be very conservative in the transitive dependencies that it introduces to relieve such problems? That would help a lot, thank you!
As a side note, is there an expectation that an organization uses all the different error-prone libraries (-annotations, -core, etc) at the same version - and in that case, could you consider an internal BOM to facilitate that? Or instead do you see them more as separate, where general usage of the annotations should only depend on the latest error-prone-annotations (which doesn't pull anything transitively), and only custom defined checks should pull the other libraries, including their more problematic transitive dependencies? Thanks again!
(I am only an occasional contributor to Error Prone, but I am the cause of your problems on the Guava side, so hi :))
I gather that there are at least two things going on:
- Adding nullness information and/or a
module-infocan require changes in users. - 33.4.6 had an outright bug in its module setup (and a couple other bugs that affected Android and the optimizers commonly used for it).
The latter problem could be solved by upgrading Error Prone to 33.4.8. (And users could also upgrade manually.) I think that moving Error Prone from Guava 33.4.6 to Guava 33.4.8 would be a strict improvement. But.
The former problem doesn't normally have such an easy solution: Users just need to adapt to the nullness information and module-info. So this is an argument for jumping back to 33.4.0, which pre-dates all the nullness changes.
I don't want to speak for the actual Error Prone team, but I imagine that, in general, they'd like to be on the newest version of Guava: That ensures that they're running their open-source build against a version of Guava that's as close as possible to the one that they're using inside Google. That said, Guava sometimes goes a while without publishing a release, and I suspect that that causes trouble for Error Prone only occasionally. Additionally, since all the recent releases of Guava have intentionally been only patch releases, there is no need to upgrade to pick up any new APIs. (This will change eventually, of course.) So it should be very likely to be safe to drop back to 33.4.0. (And Error Prone itself is unlikely to care much about depending on a version of Guava with nullness information, module-info, or even migration off sun.misc.Unsafe (since most of our usages of that are in concurrent code that I'm guessing that Error Prone doesn't use).)
(Incidentally, if you've found anything interesting or surprising about the challenges of upgrading Guava, I'd be interested in hearing about it. We've known that the nullness annotations are potential trouble, mainly for Kotlin and NullAway, but we made enough other changes that could affect enough other people that I'm sure that's not all that's come up.)
in general, they'd like to be on the newest version of Guava
This is generally how I was thinking about it. I can try to publish a new release of EP that updates the Guava version soon.
Error Prone should generally be extremely compatible with different Guava versions, so if users wanted to override the Guava version with a newer or slightly older one that would probably also work fine in practice. Would that be compatible with the requirements here, or is it necessary to be using Error Prone with the exact Guava version it specifies in its pom?
I cut a release that downgrades to Guava 33.4.0: https://github.com/google/error-prone/releases/tag/v2.39.0
Thanks for downgrading, much appreciated! As for
in general, they'd like to be on the newest version of Guava
For us at least, we intend transitive dependency versions as minimum requirements, and in fact we use the maven enforcer plugin's requireUpperBounds check to make sure they all are satisfied.
This is very different from what is being suggested here, that is keeping transitive dependencies at the latest. The problem is that doing so forces the upgrade schedule on your consumers (us).
Guava needs to be updated to avoid deprecation warnings with JDK 25:
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper (file:/home/tbr/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/33.4.0-jre/3fcc0a259f724c7de54a6a55ea7e26d3d5c0cac/guava-33.4.0-jre.jar)
WARNING: Please consider reporting this to the maintainers of class com.google.common.util.concurrent.AbstractFuture$UnsafeAtomicHelper
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
The latest releases of https://github.com/googleapis/java-cloud-bom have updated to Guava v33.5.0, I am going to update Error Prone's version to match in the next release
Is this likely to be the cause of missing method errors when running ErrorProne, or is this a separate unrelated issue? Just want to check before I raise a new issue.
[INFO] --- compiler:3.14.1:compile (default-compile) @ protobuf-maven-plugin ---
[INFO] Recompiling the module because of changed source code.
[INFO] Compiling 97 source files with javac [debug deprecation parameters release 17] to target/classes
An exception has occurred in the compiler (21.0.8). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
java.lang.NoSuchMethodError: 'com.google.common.collect.ImmutableMap com.google.common.collect.ImmutableMap$Builder.buildOrThrow()'
at com.google.errorprone.scanner.ScannerSupplier.defaultSeverities(ScannerSupplier.java:67)
at com.google.errorprone.scanner.ScannerSupplier.fromBugCheckerInfos(ScannerSupplier.java:88)
at com.google.errorprone.scanner.BuiltInCheckerSuppliers.allChecks(BuiltInCheckerSuppliers.java:663)
at com.google.errorprone.scanner.BuiltInCheckerSuppliers.defaultChecks(BuiltInCheckerSuppliers.java:671)
at com.google.errorprone.ErrorProneJavacPlugin.init(ErrorProneJavacPlugin.java:35)
at jdk.compiler/com.sun.tools.javac.api.BasicJavacTask.initPlugin(BasicJavacTask.java:256)
at jdk.compiler/com.sun.tools.javac.api.BasicJavacTask.initPlugins(BasicJavacTask.java:230)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.prepareCompiler(JavacTaskImpl.java:204)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:101)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.invocationHelper(JavacTaskImpl.java:152)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)
at org.codehaus.plexus.compiler.javac.JavaxToolsCompiler.compileInProcess(JavaxToolsCompiler.java:126)
at org.codehaus.plexus.compiler.javac.JavacCompiler.performCompile(JavacCompiler.java:214)
at org.apache.maven.plugin.compiler.AbstractCompilerMojo.executeReal(AbstractCompilerMojo.java:1235)
at org.apache.maven.plugin.compiler.AbstractCompilerMojo.execute(AbstractCompilerMojo.java:708)
at org.apache.maven.plugin.compiler.CompilerMojo.execute(CompilerMojo.java:235)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:126)
at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute2(MojoExecutor.java:328)
at org.apache.maven.lifecycle.internal.MojoExecutor.doExecute(MojoExecutor.java:316)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:174)
at org.apache.maven.lifecycle.internal.MojoExecutor.access$000(MojoExecutor.java:75)
at org.apache.maven.lifecycle.internal.MojoExecutor$1.run(MojoExecutor.java:162)
at org.apache.maven.plugin.DefaultMojosExecutionStrategy.execute(DefaultMojosExecutionStrategy.java:39)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:159)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:105)
at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:193)
at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:180)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
@ascopes that looks unrelated. ImmutableMap$Builder#buildOrThrow was added in Guava 31.0, that crash means there's an older version of Guava on the same processorpath as Error Prone
Ah that is a pain, this is likely the Maven dependencies that I have to depend on to build plugins if that is the case...
Thanks for the quick reply. I'll have a look to see if I can shoehorn a newer version of Guava into the Maven Compiler Plugin to override this.
Edit: found a workaround. It is dirty but it works. Seems <dependencies/> on the compiler plugin has no impact, but adding it as an annotation processor in the list of annotation processor path dependencies fixes the issue. Thanks again.