FCM-Topics im Firebase Admin SDK für Java: Welcher Fehler, welche Reaktion?

subscribeToTopic und unsubscribeFromTopic aus firebase-admin 9.11.0 für Java melden Fehler als reason-String pro Token. Dieser Artikel ordnet jede Antwort der Reaktion zu, die das Backend zeigen sollte.

Googles Doku hilft dabei kaum: Sie erwähnt getReason(), nennt aber weder die möglichen Werte noch, was man mit ihnen tun soll.

Grundlage sind Tests auf einem Android-Gerät und der Quellcode des SDK.

Vorab

  • Fehler vom Server werfen keine Exception. Sie werden als Liste mit einem Eintrag je Token zurückgemeldet.
  • getIndex() zeigt auf die Position des Tokens in der übergebenen Liste.
  • Subscribe und Unsubscribe sind idempotent, ein Retry ist also gefahrlos.
TopicManagementResponse response = messaging.subscribeToTopic(tokens, "news");
for (TopicManagementResponse.Error error : response.getErrors()) {
    String token = tokens.get(error.getIndex());
    String reason = error.getReason();
    // Reaktion je nach reason, siehe unten
}

Erfolg, nichts zu tun

  • Kein Eintrag in getErrors(), getSuccessCount() zählt den Token mit.
  • Subscribe auf ein schon abonniertes Topic: Der Server antwortet 409 ALREADY_EXISTS, das SDK wertet das als Erfolg.
  • Unsubscribe von einem nie abonnierten Topic: Erfolg.

Erfolg heißt nicht, dass die App noch installiert ist. Direkt nach der Deinstallation und solange das Gerät offline ist, gelingen die Aufrufe weiter.

Aufruf schlägt permanent fehl

Mit dem Token ist nichts mehr anzufangen. Er wird nicht mehr benötigt.

  • registration-token-not-registered: App deinstalliert, deleteToken() aufgerufen, App-Daten gelöscht. Bei Unsubscribe ist das Abo damit erledigt.
  • invalid-argument: Der String ist kein gültiger Token.

Scheitern alle Tokens eines Aufrufs mit demselben Grund, liegt es vermutlich nicht an den Tokens. Dann sollte man zuerst die Konfiguration prüfen.

Später erneut versuchen

Der Token ist grundsätzlich noch korrekt. Bei einem Retry zu einem späteren Zeitpunkt klappt eine Aktion möglicherweise wieder.

  • internal-error
  • unavailable: Antwortet Google mit 503, wiederholt das SDK selbst viermal, insgesamt also fünf Versuche.
  • deadline-exceeded
  • resource-exhausted
  • quota-exceeded
  • aborted
  • cancelled
  • failed-precondition
  • unknown-error: Netzwerkfehler, unbekannter HTTP-Status oder eine Antwort, die das SDK nicht lesen kann
  • FirebaseMessagingException mit getErrorCode() == ErrorCode.CANCELLED: Der wartende Thread wurde unterbrochen. Einen Teil der Tokens kann das SDK schon verarbeitet haben.
  • IllegalStateException „FirebaseApp … was deleted“

Jeder andere Status des Servers landet in Kleinbuchstaben mit Bindestrich in reason, etwa out-of-range.

Konfiguration korrigieren

  • permission-denied: Die IAM-Rechte fehlen, Einstellungen prüfen.
  • unauthenticated: Die hinterlegten Firebase-Credentials sind ungültig.
  • IllegalArgumentException „Project ID is required …“

Eigenen Code korrigieren

Diese Fälle prüft das SDK vor dem Request und wirft IllegalArgumentException:

  • Ein leerer oder null-Token in der Liste. Schon einer lässt den ganzen Aufruf scheitern.
  • Eine leere Liste oder mehr als 1000 Tokens.
  • Ein Topic-Name, der nicht auf ^(/topics/)?(private/)?[a-zA-Z0-9-_.~%]+$ passt.

Setup

  • firebase-admin 9.11.0, Java 21
  • Android-Gerät mit Android 16 und Google Play Services
  • Capacitor-App mit @capacitor-firebase/messaging 8.4.0

Für Anmerkungen oder Feedback schreibt mir gerne unter info (at) leonardw (punkt) de