apereo/cas · warning

Notification [ ] sent to [ ] rejected with error: [ ]

Error message

Notification [{}] sent to [{}] rejected with error: [{}]

What it means

APNMessagingNotificationSender sends a push notification via the ApnsClient and inspects the async response. If Apple's APNs rejects the notification, it warns 'Notification [{}] sent to [{}] rejected with error: [{}]' with the rejection reason (e.g. BadDeviceToken, TopicDisallowed). The message was delivered to APNs but not accepted for delivery.

Solutions

  1. Read the rejection reason in the log and act on it (BadDeviceToken → remove/refresh the device token)
  2. Verify the APNs topic matches the iOS app bundle id and the signing key/team configuration
  3. Ensure device tokens are current and re-registered by the app
  4. Check you are using the correct APNs environment (development vs production) endpoint/credentials

Example fix

// before
cas.notifications.apn.topic=com.old.bundle
// after
cas.notifications.apn.topic=com.example.correctbundle
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-send sanity checks
if (!StringUtils.hasText(deviceToken) || deviceToken.length() < 32) return;
if (!StringUtils.hasText(apnsProperties.getTopic())) throw new IllegalStateException("APNs topic required");

Try / catch

apnsClient.sendNotification(n).whenComplete((resp, cause) -> {
    if (cause != null) { LOGGER.error("APNs send failed", cause); }
    else if (!resp.isAccepted()) { LOGGER.warn("APNs rejected: {}", resp.getRejectionReason()); }
});

Prevention

When it happens

Trigger: apnsClient.sendNotification completes with a response where isAccepted() is false; the rejection reason from APNs (invalid device token, wrong topic/bundle id, expired token, payload issues) is logged.

Common situations: Stale device tokens for users who uninstalled the app, mismatched APNs topic vs the app's bundle identifier, wrong signing key/team settings, or environment mismatch (development token sent to production APNs).


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/398c36df848660b8. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-notifications-apn/src/main/java/org/apereo/cas/notifications/APNMessagingNotificationSender.java:46

    @Override
    @SuppressWarnings("FutureReturnValueIgnored")
    public boolean notify(final Principal principal, final Map<String, String> messageData) {
        val deviceToken = principal.getSingleValuedAttribute(properties.getRegistrationTokenAttributeName(), String.class);
        val payload = new SimpleApnsPayloadBuilder()
            .setAlertTitle(messageData.get("title"))
            .setAlertBody(messageData.get("message"))
            .build();

        val pushNotification = new SimpleApnsPushNotification(deviceToken, properties.getTopic(), payload);
        LOGGER.trace("Sending push notification to [{}] with payload [{}]", principal, pushNotification);

        apnsClient.sendNotification(pushNotification)
            .whenComplete((response, cause) -> {
                if (response != null) {
                    if (response.isAccepted()) {
                        LOGGER.debug("Notification [{}] sent to [{}] is accepted successfully", pushNotification, principal.getId());
                    } else {
                        LOGGER.warn("Notification [{}] sent to [{}] rejected with error: [{}]",
                            pushNotification, principal.getId(), response.getRejectionReason().orElse(StringUtils.EMPTY));
                    }
                } else {
                    LoggingUtils.error(LOGGER, cause);
                }
            });
        return true;
    }
}

View on GitHub (pinned to e7288fc434)