Skip to content

Fatal exception in SPTAppRemoteUserCapabilities when can_play_on_demand is null #467

@edwinveger

Description

@edwinveger

Summary

The SDK crashes for roughly 0.5% of our users with the following fatal exception:

Fatal Exception: Malformed payload
Expected non-optional parameter can_play_on_demand to be of type NSNumber, but found (null)

We are using the SDK in the intended way: users authorize our app to play music on their behalf. Despite this, the SDK appears to receive a payload from Spotify where can_play_on_demand is null, and it crashes rather than falling back to a sensible default (e.g. false).

Expected behavior

When the backend returns a null value for can_play_on_demand, the SDK should default to false (or otherwise surface the missing capability gracefully) instead of throwing a fatal exception that takes down the host application.

Actual behavior

The SDK throws a fatal exception the moment capabilities.canPlayOnDemand is accessed, crashing our app.

Reproduction

The crash occurs in our SPTAppRemoteUserAPIDelegate implementation when reading canPlayOnDemand from the capabilities passed into the delegate callback:

extension SpotifyClient: SPTAppRemoteUserAPIDelegate {
    nonisolated func userAPI(
        _ userAPI: any SPTAppRemoteUserAPI,
        didReceive capabilities: any SPTAppRemoteUserCapabilities
    ) {
        Task { @MainActor in
            self.hasOnDemandCapability = capabilities.canPlayOnDemand // <-- fatal exception
            // …
        }
    }
}

We need canPlayOnDemand to tailor the user experience, so there is no workaround on our side — the property cannot be safely read at all for this subset of users.

We don't have information about which specific API call triggers the malformed payload; the crash surfaces only through the delegate callback.

Environment

  • Spotify iOS SDK: 5.0.1
  • iOS versions seen in the wild: 18.6, 18.7, 26.1, 26.2, 26.3, and others
  • Affected users: ~0.5% of sessions

Suggested fix

Treat a null can_play_on_demand in the incoming payload as false (or expose it as an optional) rather than crashing. This seems like a straightforward defensive fix on the SDK side.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions