Skip to content

[BUG] AppleInstallation createOrUpdateInstallation fails with “Platform property is not specified or given value is not supported” #191

@vodidan

Description

@vodidan

Describe the bug
When creating an iOS installation using AppleInstallation via createOrUpdateInstallation(...), Azure Notification Hubs REST API returns:
'Platform' property is not specified or given value is not supported.

This occurs even though AppleInstallation is used and the platform is correctly set internally.

There appears to be an inconsistency between:

  • AppleInstallation (which uses "apple" as platform internally in BaseInstallationDeserializer.java‎)
  • NotificationPlatform.Apns (serialized as "apns")
  • REST API validation expectations

The SDK serialization behavior does not align with the REST Installations API requirements.

Exception or Stack Trace

2026-02-24T10:26:42.3826749Z com.windowsazure.messaging.NotificationHubsException: Tracking ID: <id> Error: HTTP/2.0 400 Bad Request - <Error xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Code>400</Code><Detail>The provided request body is invalid. Error: 'Platform' property is not specified or given value is not supported.. TrackingId:<id>,TimeStamp:2/24/2026 10:26:42 AM +00:00</Detail></Error>
2026-02-24T10:26:42.3826767Z 	at com.windowsazure.messaging.NotificationHubsService$1.completed(NotificationHubsService.java:69) ~[na:na]
2026-02-24T10:26:42.3827203Z 	at com.windowsazure.messaging.NotificationHubsService$1.completed(NotificationHubsService.java:63) ~[na:na]
2026-02-24T10:26:42.3827228Z 	at org.apache.hc.core5.concurrent.BasicFuture.completed(BasicFuture.java:148) ~[application:5.3.6]
2026-02-24T10:26:42.3827245Z 	at org.apache.hc.core5.concurrent.ComplexFuture.completed(ComplexFuture.java:72) ~[application:5.3.6]
2026-02-24T10:26:42.3827264Z 	at org.apache.hc.client5.http.impl.async.InternalAbstractHttpAsyncClient$2$1.completed(InternalAbstractHttpAsyncClient.java:321) ~[na:na]
2026-02-24T10:26:42.3827282Z 	at org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer$1.completed(AbstractAsyncResponseConsumer.java:101) ~[na:na]
2026-02-24T10:26:42.3827300Z 	at org.apache.hc.core5.http.nio.entity.AbstractBinAsyncEntityConsumer.completed(AbstractBinAsyncEntityConsumer.java:87) ~[application:5.3.6]
2026-02-24T10:26:42.3827319Z 	at org.apache.hc.core5.http.nio.entity.AbstractBinDataConsumer.streamEnd(AbstractBinDataConsumer.java:83) ~[application:5.3.6]
2026-02-24T10:26:42.3827337Z 	at org.apache.hc.core5.http.nio.support.AbstractAsyncResponseConsumer.streamEnd(AbstractAsyncResponseConsumer.java:142) ~[application:5.3.6]
2026-02-24T10:26:42.3827355Z 	at org.apache.hc.client5.http.impl.async.H2AsyncMainClientExec$1.streamEnd(H2AsyncMainClientExec.java:186) ~[na:na]
2026-02-24T10:26:42.3827450Z 	at org.apache.hc.core5.http2.impl.nio.ClientH2StreamHandler.consumeData(ClientH2StreamHandler.java:242) ~[na:na]
2026-02-24T10:26:42.3827471Z 	at org.apache.hc.core5.http2.impl.nio.AbstractH2StreamMultiplexer$H2Stream.consumeData(AbstractH2StreamMultiplexer.java:1661) ~[na:na]
2026-02-24T10:26:42.3827489Z 	at org.apache.hc.core5.http2.impl.nio.AbstractH2StreamMultiplexer.consumeDataFrame(AbstractH2StreamMultiplexer.java:1051) ~[application:5.3.6]
2026-02-24T10:26:42.3827507Z 	at org.apache.hc.core5.http2.impl.nio.AbstractH2StreamMultiplexer.consumeFrame(AbstractH2StreamMultiplexer.java:740) ~[application:5.3.6]
2026-02-24T10:26:42.3827525Z 	at org.apache.hc.core5.http2.impl.nio.AbstractH2StreamMultiplexer.onInput(AbstractH2StreamMultiplexer.java:448) ~[application:5.3.6]
2026-02-24T10:26:42.3827545Z 	at org.apache.hc.core5.http2.impl.nio.AbstractH2IOEventHandler.inputReady(AbstractH2IOEventHandler.java:65) ~[application:5.3.6]
2026-02-24T10:26:42.3827562Z 	at org.apache.hc.core5.http2.impl.nio.ClientH2IOEventHandler.inputReady(ClientH2IOEventHandler.java:39) ~[na:na]
2026-02-24T10:26:42.3827580Z 	at org.apache.hc.core5.reactor.ssl.SSLIOSession.decryptData(SSLIOSession.java:618) ~[application:5.3.6]
2026-02-24T10:26:42.3827597Z 	at org.apache.hc.core5.reactor.ssl.SSLIOSession.access$200(SSLIOSession.java:74) ~[application:5.3.6]
2026-02-24T10:26:42.3827614Z 	at org.apache.hc.core5.reactor.ssl.SSLIOSession$1.inputReady(SSLIOSession.java:204) ~[na:na]
2026-02-24T10:26:42.3827684Z 	at org.apache.hc.core5.reactor.InternalDataChannel.onIOEvent(InternalDataChannel.java:139) ~[na:na]
2026-02-24T10:26:42.3827702Z 	at org.apache.hc.core5.reactor.InternalChannel.handleIOEvent(InternalChannel.java:51) ~[application:5.3.6]
2026-02-24T10:26:42.3827717Z 	at org.apache.hc.core5.reactor.SingleCoreIOReactor.processEvents(SingleCoreIOReactor.java:176) ~[na:na]
2026-02-24T10:26:42.3827735Z 	at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:125) ~[na:na]
2026-02-24T10:26:42.3827752Z 	at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:92) ~[application:5.3.6]
2026-02-24T10:26:42.3827768Z 	at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44) ~[na:na]
2026-02-24T10:26:42.3827956Z 	at java.base@25.0.2/java.lang.Thread.runWith(Thread.java:1487) ~[application:na]
2026-02-24T10:26:42.3827977Z 	at java.base@25.0.2/java.lang.Thread.run(Thread.java:1474) ~[application:na]
2026-02-24T10:26:42.3827995Z 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:832) ~[application:na]
2026-02-24T10:26:42.3828070Z 	at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:808) ~[application:na]
2026-02-24T10:26:42.3828084Z

To Reproduce
Steps to reproduce the behavior:

  • Spring boot 4/GraalVM native image
  • Create an AppleInstallation
  • Call createOrUpdateInstallation(...)
  • Receive validation error from service

Code Snippet

public void registerDevice(String installationId, String deviceToken,
                           String userId, List<String> tags) {

    try {
        AppleInstallation installation = new AppleInstallation(installationId);

        installation.setPushChannel(deviceToken);

        if (tags != null && !tags.isEmpty()) {
            for (String tag : tags) {
                installation.addTag(tag);
            }
        }

        if (userId != null && !userId.isEmpty()) {
            installation.setUserId(userId);
        }

        hub().createOrUpdateInstallation(installation);

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Expected behavior
The installation should be successfully created when using AppleInstallation with a valid APNS device token.

The SDK should serialize the platform value in a way that is accepted by the Notification Hubs Installations REST API.

Setup (please complete the following information):

  • OS: Linux (Docker container)
  • Framework: Spring Boot 4/Maven (native image build)
  • Build: GraalVM 25 Community (native image, built via Dockerfile)
  • IDE: IntelliJ IDEA
  • Library: com.github.Azure:azure-notificationhubs-java-backend:1.0.5 (GitHub/JitPack-style build)

Additional context
Add any other context about the problem here.

Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issue as an incomplete report. Please do not provide any private information in this bug report.

  • Bug Description Added
  • Repro Steps Added
  • Setup information Added

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions