Describe your environment
- Operating System version: Amazon Linux 2023 (AWS ECS Fargate), also reproduced on macOS 15
- Firebase SDK version: 9.7.1 (the code in question is unchanged on
v9.10.0)
- Library version:
com.google.firebase:firebase-admin:9.7.1, Java 25, Spring Boot 4.1
- Firebase Product: messaging (FCM only — no Firestore, Storage, Realtime Database or FirebaseAuth)
Describe the problem
google-http-client-jackson2 is a hard runtime requirement, and FirebaseOptions.setJsonFactory(...) is not sufficient to avoid it. Excluding the artifact still throws ClassNotFoundException on the first FCM send, which forces ~580 KB of Jackson 2 into deployments whose Firebase usage is otherwise entirely Gson-based.
Steps to reproduce:
- Configure
FirebaseOptions with an explicit transport and JSON factory (see code below).
- Exclude
com.google.http-client:google-http-client-jackson2 from the build.
- Start the application — startup succeeds, both
FirebaseApp instances initialize fine.
- Send a message via
FirebaseMessaging.sendEachForMulticast(...) — fails:
java.lang.ClassNotFoundException: com.google.api.client.json.jackson2.JacksonFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:580)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:490)
at com.google.firebase.messaging.FirebaseMessagingClientImpl.fromApp(FirebaseMessagingClientImpl.java:193)
at com.google.firebase.messaging.FirebaseMessaging$7.get(FirebaseMessaging.java:637)
at com.google.firebase.messaging.FirebaseMessaging$7.get(FirebaseMessaging.java:634)
at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers.java:201)
at com.google.firebase.messaging.FirebaseMessaging.getMessagingClient(FirebaseMessaging.java:519)
at com.google.firebase.messaging.FirebaseMessaging.sendOpForSendResponse(FirebaseMessaging.java:253)
The failure only surfaces on the first send, because getMessagingClient() is lazy and memoized — so a startup smoke test does not catch it.
The configured factory does appear to be honoured for messaging itself; something in the request-factory / credentials initialization path loads JacksonFactory regardless of configuration.
Relevant Code:
FirebaseOptions.builder()
.setHttpTransport(new NetHttpTransport())
.setJsonFactory(GsonFactory.getDefaultInstance())
.setCredentials(GoogleCredentials.fromStream(serviceAccountJson))
.build();
We use FCM only, and prune the parts of the SDK we do not need:
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<exclusions>
<exclusion>
<groupId>com.google.cloud</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
Why this matters
After that pruning, everything remaining is Gson-based. Credentials come from GoogleCredentials, and google-auth-library-oauth2-http uses GsonFactory (OAuth2Utils.JSON_FACTORY), which brings google-http-client-gson + gson 2.13.2 (283 KB) in at compile scope. Despite that, we must also ship:
+- com.google.http-client:google-http-client-jackson2:jar:runtime
| \- com.fasterxml.jackson.core:jackson-core:jar:2.21.4:runtime <- 580 KB, sole consumer
Jackson 2 is roughly twice the size of the Gson stack it duplicates, and nothing else on our classpath requires it.
This is amplified on Spring Boot 4.x, which moved to Jackson 3 (tools.jackson). Applications now carry both Jackson generations side by side, with Jackson 2 present solely to satisfy firebase-admin.
The blocker cited in the code may be stale
public static JsonFactory getDefaultJsonFactory() {
// Force using the Jackson2 parser for this project for now. Eventually we should switch
// to Gson, but there are some issues that are preventing this migration at the moment.
// See https://github.com/googleapis/google-api-java-client/issues/1779 for details.
return JacksonFactory.getDefaultInstance();
}
googleapis/google-api-java-client#1779 was closed on 2021-04-29 as triage rather than fixed, splitting into three sub-bugs. Per a 2023-03-03 comment on that issue from a Gson maintainer:
ApiClientUtils.java does not appear to have been revisited on this point since (subsequent commits touch transports and copyright headers).
Ask
Any of these would help:
- Make the Jackson2 dependency genuinely optional when a
JsonFactory is supplied via FirebaseOptions.
- Switch the default to
GsonFactory, if the 2021 blockers no longer apply.
- Failing both, document that
google-http-client-jackson2 is a hard requirement — that alone would save others the investigation.
Describe your environment
v9.10.0)com.google.firebase:firebase-admin:9.7.1, Java 25, Spring Boot 4.1Describe the problem
google-http-client-jackson2is a hard runtime requirement, andFirebaseOptions.setJsonFactory(...)is not sufficient to avoid it. Excluding the artifact still throwsClassNotFoundExceptionon the first FCM send, which forces ~580 KB of Jackson 2 into deployments whose Firebase usage is otherwise entirely Gson-based.Steps to reproduce:
FirebaseOptionswith an explicit transport and JSON factory (see code below).com.google.http-client:google-http-client-jackson2from the build.FirebaseAppinstances initialize fine.FirebaseMessaging.sendEachForMulticast(...)— fails:The failure only surfaces on the first send, because
getMessagingClient()is lazy and memoized — so a startup smoke test does not catch it.The configured factory does appear to be honoured for messaging itself; something in the request-factory / credentials initialization path loads
JacksonFactoryregardless of configuration.Relevant Code:
We use FCM only, and prune the parts of the SDK we do not need:
Why this matters
After that pruning, everything remaining is Gson-based. Credentials come from
GoogleCredentials, andgoogle-auth-library-oauth2-httpusesGsonFactory(OAuth2Utils.JSON_FACTORY), which bringsgoogle-http-client-gson+gson2.13.2 (283 KB) in at compile scope. Despite that, we must also ship:Jackson 2 is roughly twice the size of the Gson stack it duplicates, and nothing else on our classpath requires it.
This is amplified on Spring Boot 4.x, which moved to Jackson 3 (
tools.jackson). Applications now carry both Jackson generations side by side, with Jackson 2 present solely to satisfy firebase-admin.The blocker cited in the code may be stale
googleapis/google-api-java-client#1779 was closed on 2021-04-29 as triage rather than fixed, splitting into three sub-bugs. Per a 2023-03-03 comment on that issue from a Gson maintainer:
ApiClientUtils.javadoes not appear to have been revisited on this point since (subsequent commits touch transports and copyright headers).Ask
Any of these would help:
JsonFactoryis supplied viaFirebaseOptions.GsonFactory, if the 2021 blockers no longer apply.google-http-client-jackson2is a hard requirement — that alone would save others the investigation.