Calls to messaging.cancel always returns error 5040

We have both an iOS and Android app that uses Backendless to schedule messages for delivery. We also allow the user to cancel these scheduled messages. However, the call to cancel a message on iOS now just returns error 5040 for every cancel request. We’re using the Backendless SDK v3.0.46 pulled down through Cocoapod with Backendless 3.X.

Has something changed or is this a bug in the latest SDK?

backendless.messaging.cancel(scheduledMessage.objectId!,
                                
    response: { (entity: Any?) -> Void in
        
        self.removeScheduledMessage(scheduledMessage: scheduledMessage,
                                
            completion: {
                completion()
            },
            
            error: { (message) -> Void in
                error(message)
            })
    },
    
    error: { (fault: Fault?) -> Void in
        
        if fault?.faultCode == "5040" {
                    
            // If the error code is 5040, the messages has either already been canceled or
            // has already been delivered. Either way, treat it as a success and remove
            // the entry for it.
            self.removeScheduledMessage(scheduledMessage: scheduledMessage,
                                    
                completion: {
                    completion()
                },
                
                error: { (message) -> Void in
                    error(message)
                })
            
        } else {
            error((fault?.message)!)
        }
    })

Does the error happen for a message scheduled for future delivery (say in 24 hours)?

Yes. Even messages scheduled for 2 days in the future return this error 5040. When I first wrote the code, I was able to cancel a message scheduled at only 1 or 2 minutes in the future. I did these short time intervals a lot during initial testing.

Now, on Android, the same code works fine and the message is canceled:

Backendless.Messaging.cancel(scheduledMessage.getMessageId(), new AsyncCallback<MessageStatus>() {
@Override
public void handleResponse(MessageStatus response) {

    removeScheduledMessage(scheduledMessage, new RemoveScheduledMessageResponseCallback() {
        @Override
        public void onResponse() {
            cancelScheduledMessageResponseCallback.onResponse();
        }
    }, new FaultCallback() {
        @Override
        public void onFault(String code, String message) {
            faultCallback.onFault(code, message);
        }
    });
}

@Override
public void handleFault(BackendlessFault fault) {

    if(fault.getCode().equals("5040")) {

        // If the error code is 5040, the messages has either already been canceled or
        // has already been delivered. Either way, treat it as a success and remove
        // the entry for it.
        removeScheduledMessage(scheduledMessage, new RemoveScheduledMessageResponseCallback() {
            @Override
            public void onResponse() {
                cancelScheduledMessageResponseCallback.onResponse();
            }
        }, new FaultCallback() {
            @Override
            public void onFault(String code, String message) {
                faultCallback.onFault(code, message);
            }
        });

    } else {
        faultCallback.onFault(fault.getCode(), fault.getMessage());
    }
}

});

Oops, lets try it again with formatting:

Backendless.Messaging.cancel(scheduledMessage.getMessageId(), new AsyncCallback&lt;MessageStatus&gt;() {
    @Override
    public void handleResponse(MessageStatus response) {


        removeScheduledMessage(scheduledMessage, new RemoveScheduledMessageResponseCallback() {
            @Override
            public void onResponse() {
                cancelScheduledMessageResponseCallback.onResponse();
            }
        }, new FaultCallback() {
            @Override
            public void onFault(String code, String message) {
                faultCallback.onFault(code, message);
            }
        });
    }


    @Override
    public void handleFault(BackendlessFault fault) {


        if(fault.getCode().equals("5040")) {


            // If the error code is 5040, the messages has either already been canceled or
            // has already been delivered. Either way, treat it as a success and remove
            // the entry for it.
            removeScheduledMessage(scheduledMessage, new RemoveScheduledMessageResponseCallback() {
                @Override
                public void onResponse() {
                    cancelScheduledMessageResponseCallback.onResponse();
                }
            }, new FaultCallback() {
                @Override
                public void onFault(String code, String message) {
                    faultCallback.onFault(code, message);
                }
            });


        } else {
            faultCallback.onFault(fault.getCode(), fault.getMessage());
        }
    }
});

Thanks, we will investigate and report back

Did you notice if the message is still delivered after you cancel it?

For my shorter tests of 2, 5, and 10 minutes, yes the messages still get delivered, which is the real problem. For the longer tests, I don’t know yet.

We made a few changes. Could you please try again?

I’m still getting the 5040 error codes. I sent a message for 5 minutes in the future and 30. How long can you wait to cancel before it’s too late to do so?

It should work in both of these cases. We will continue the investigation. Meanwhile, could you also a rest request to cancel?

https://backendless.com/documentation/messaging/rest/messaging_cancel_scheduled_message.htm

I scheduled a message via the app and then took the message id and canceled it with curl. The message wasn’t delivered so it seems to have worked via curl.

Since I was able to cancel a message via curl at the command line, I decided to attempt the same thing from my Swift code via a REST call, but it also gets the error 5040 code and fails to cancel:


// HACK: Use Alamofire to cancel a message via REST since calling backendless.messaging.cancel doesn't seem to work.
        
// curl -H application-id:4BE04D0E-06FB-A8F0-FFB5-63FD97238000 -H secret-key:4&lt;my-secret-key&gt; -X DELETE -v https://api.backendless.com/v1/messaging/D5764B89-DD80-6009-FF79-857134A33D00



let headers: HTTPHeaders = [
    "application-type": "REST",
    "application-id": BACKENDLESS_APP_ID,
    "secret-key": BACKENDLESS_SECRET_KEY
    //"secret-key": BACKENDLESS_REST_SECRET_KEY
]


Alamofire.request("https://api.backendless.com/v1/messaging/\(scheduledMessage.objectId!)", method: .delete, headers: headers)
    .responseJSON { response in
        
        print("response = \(response)")
        
        //print(response.request)  // original URL request
        //print(response.response) // URL response
        //print(response.data)     // server data
        //print(response.result)   // result of response serialization
        
        if response.error == nil {
            
            //print(response.result.isSuccess)
            //print(response.result.description)
            
            if response.result.isSuccess {


                self.removeScheduledMessage(scheduledMessage: scheduledMessage,


                    completion: {
                        completion()
                    },


                    error: { (message) -> Void in
                        error(message)
                    })
                
            } else {
                error("Failed to cancel scheduled message.")
            }
            
        } else {
            print("Failed to get a value from the response.")
            error("Failed to cancel scheduled message.")
        }
    }
    

Any news on why this happens?

Hi, Kevin.
Please, could you clarify, what is “scheduledMessage.objectId!” in your code ?
“scheduledMessage” should be a MessageStatus object, and it hasn’t property “objectId”, only “messageId”.

Whatever it was, we couldn’t reproduce this on both 3 and 4 Backendless versions.
Could you try the same code on the new applications in 3 and 4 Backendless versions.