Context not passed in when makeWebRequest encounters system-level errors

When using the new `context` option to chain requests, the callback is expected to accept a new `context` argument. For application-level errors, like a 404, the `context` gets passed in correctly. However, for OS-level errors (with negative error codes), the `context` doesn't get passed in, causing a `Not Enough Arguments Error` to be raised.

The following PoC below will trigger it.

The basic idea is to generate a system-level error by disabling BLE and Wifi in the simulator and making a web-request. Our callback will be called with the wrong number of arguments, generating the error.

This is observed in the beta 1 version of ConnectIQ 3.0.

using Toybox.Application;

using Toybox.Communications;




class ContextBugApp extends Application.AppBase {




function initialize() {

AppBase.initialize();

}




// Trigger bug by setting the following in simulator:

// Wifi: Not Connected

// BLE: Not Connected

//

// Expected Result: responseCode = -104 with `context` passed in

// Actual Result: `Not Enough Arguments Error`

function onResponse(responseCode, data, context) {

System.println("onResponse: " + { "responseCode" => responseCode,

"data" => data,

"context" => context });

}




function onStart(state) {

var url = "jsonplaceholder.typicode.com/.../1";

var parameters = {};

var options = {

:method => Communications.HTTP_REQUEST_METHOD_GET,

:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,

:context => { :foo => "bar" }

};

var responseCallback = method(:onResponse);

Communications.makeWebRequest(url, parameters, options, responseCallback);

}




function onStop(state) {

}




function getInitialView() {

return [ new ContextBugView() ];

}




}


Side-note: Changing the arity of the callback depending on whether a `context` is used seems like a really brittle design due to these kinds of bugs. Seems like it'd be better for an `onResponse` callback to have a fixed specification, something like:

// Unlike canonical spec, `context` is *always* passed in, it's just null if a context wasn't used.
function onResponse(responseCode, data, context) {
if (context == null) {
// Do something
} else {
// Do something else
}
}
  • if you see an IQ!, what do you see in the CIQ_LOG?  

  • Error: Not Enough Arguments Error
    Details: 'Failed invoking <symbol>'
    Same error reported in this thread.

  • And when I search for the PC the function is the onReceive callback.

  • yes, I had the same error.

    In my case I don't analyse data in onReceive only pack it with context and return to app (from background) hence decision to have one onReceive. If you want only to have one communication class you use different :method in makeWebRequest

  • This issue has re-surfaced in another post, so I'm updating here... As I suggested years ago, you can get an additional context parameter if you want it, all you need is a delegate and a wrapper function.

    class WebRequestDelegate {
        typedef WebRequestCallback as Method(
            responseCode as Number,
            data as Lang.Dictionary or Lang.String or PersistedContent.Iterator or Null,
            context as Object or Null
        );
    
        hidden var mCallback as WebRequestCallback; // function always takes 3 arguments
        hidden var mContext as Object or Null; // this is the 3rd argument
    
        function initialize(context as Object or Null) {
            mContext = context;
        }
    
        function makeWebRequest(url as String, parameters as Dictionary or Null, options as Dictionary or Null, callback as WebRequestCallback) as Void {
            mCallback = callback;
            Communications.makeWebRequest(url, params, options, self.method(:onWebResponse));
        }
    
        function onWebResponse(code as Number, data as Lang.Dictionary or Lang.String or PersistedContent.Iterator or Null) as Void {
            mCallback.invoke(code, data, mContext);
        }
    }
    
    
    // call this instead of Communications.makeWebRequest. it always takes a context
    function makeWebRequest(url as String, parameters as Dictionary or Null, options as Dictionary or Null, callback as WebRequestCallback, context as Object or Null) as Void {
        var delegate = new WebRequestDelegate(context);
        delegate.makeWebRequest(url, params, options, callback);
    }
    


  • Thanks! Any insight into why the version of onReceive() with the callback parameter hasn't been deprecated, either "officially" (via the SDK/compiler) or in the documentation?