Crash only on first start: File no found / Failed invoking

Hi there,

I am encountering a strange problem that I'm not able to solve after hours of tinkering around:

I have a watchface that, when installed immediately shows a CIQ error screen. When I go to the watchface selection on the device it works just fine from then on.

The CIQ_LOG.YML shows:

Error: File Not Found Error
Details: 'Failed invoking <symbol>'
Time: 2022-04-07T13:50:06Z
Part-Number: 006-B3943-00
Firmware-Version: '8.13'
Language-Code: deu
ConnectIQ-Version: 4.1.1

No stack, nothing.

My watchface has a background process and a lot of settings. The onSettingsChanged method sets some values with Storage.setValue() to make them available to the background.

Interestingly, everything almost works fine when I run it in the simulator, only that if I didnt run it for a while, the debug will say "Encountered app crash" without further message - when that happens, the simulator is still running normally and I can continue triggering background processes or whatsoever.

It almos seems like a race condition where the background process is calling onBackground when the app is not yet ready for it... Here's what the simulator outputs:

***INITIALIZE***
***STARTUP***
***STARTUP FINISHED***
getInitialView
****background registered****
Lastlocupdate is null
Restored solar info failed - recomputing...
Background: onReceive... 
Background: Response: 401

Encountered app crash.

Here's some of the code:

function initialize() {
        System.println("***INITIALIZE***");
        AppBase.initialize();
        
    }

    // onStart() is called on application start up
    function onStart(state as Dictionary?) as Void {
    System.println("***STARTUP***");
    
    System.println("***STARTUP FINISHED***");
    }

    // onStop() is called when your application is exiting
    function onStop(state as Dictionary?) as Void {
    }

    // Return the initial view of your application here
    function getInitialView() as Array<Views or InputDelegates>? {
        System.println("getInitialView");
        $.gDeviceSettings = System.getDeviceSettings();
        if(inBackground == false){
            // getAllSettings();
            //register for temporal events if they are supported
            if(Toybox.System has :ServiceDelegate) {
                canDoBG=true;
                Background.registerForTemporalEvent(new Time.Duration(10 * 60));
                System.println("****background registered****");
            } else {
                System.println("****background not available on this device****");
            }
        }
        return [ new TAWATXView() ] as Array<Views or InputDelegates>;
    }

I'd appreciate any hints! I have tried sideloading but it is the same behaviour - also my ERA viewer won't start after I input my information... It say collecting data and then crashes...

Thank you so much!

  • Do you have all the proper annotations for (:background)? I don't see it in the code you posted.

    If you have the background permission set, the VM will try to call onAppUpdate() and/or onAppInstall(), and attempt to load your entire app as a background service without the annotations.

    If you're basising this on teh example code I did a few years back, getInitialView() always rund in the main app so no need to use "inBackground" there.  Some things in the AppBase only run in the backgroung (getServiceDelegate), so only in the main app (getInitialView and onBackgroundData for example), and some for both (onStart onStop, initialize). I only really use inBackground in onStop to see if I should delete the temporal event - to see if I'm in the main app

  • Hi Jim and thanks for the quick reply!

    I have the following decorations:

    (:background)
    var inBackground=false;
    (:background)
    class TAWATXApp extends Application.AppBase {
    
    ...
    
    
    different .mc file:
    (:background)
    class TAWATXServiceDelegate extends Toybox.System.ServiceDelegate {
    
    ...
    
    (:background)
    class JsonTransaction {

    I have seen that there is a Monkey C type checker that I can change to be stricter  but how do I do that in VSCode?

    Does this also need a background decoration:

    function getApp() as TAWATXApp {
        return Application.getApp() as TAWATXApp;
    }

    Thank you so much!!!

  • In VS code, for the Monkey C Extention, you can set compiler flags, with -l 0 being no type checking, and -l 3, the max.

    But this isn't anything you'll find with type checking.

    What is your target device where you see this?  It could be related to that as well as what you are doing in the service delegate.  For example, doing a setValue() in the background on some devices can cause an exception.

    Also, does the background ever actually run?  Looks like you're going for every 10 minutes.

  • My only target device is the epix2. The Background is running every 10 minutes reliably and the data from openweathermap is arriving as expected. In the beginning, the app has no location and the owm request fails as expected. I can see it comes all the way to Background.exit(data) but then it never arrives in onBackgrounddata (maybe because data is null?) and next thing it crashes.

    I had some setValue() in the background but commeting them out did not help...

    Unfortunately the forum will not let me post my code anymore.. It says I'm blocked?

  • Here's my Code without formatting (no idea why it won't let me...)
    import Toybox.System;
    import Toybox.Background;
    import Toybox.Communications;
    import Toybox.Lang;
    using Toybox.Application.Storage as Strg;

    (:background)
    class TAWATXServiceDelegate extends Toybox.System.ServiceDelegate {
       
        function initialize() {
            System.ServiceDelegate.initialize();
            inBackground=true;              //trick for onExit()
        }
       
        function onTemporalEvent() {
            System.println("****background running****");
            var jt = new JsonTransaction();
            var data = jt.makeRequest();
        }
       

    }

    (:background)
    class JsonTransaction {
        // set up the response callback function
        function onReceive(responseCode as Number, data as Dictionary?) as Void {
            System.println("onReceive... ");
            if (responseCode == 200) {
                System.println("Request Successful");                   // print success
                // System.println("Data: " + data);
                // Strg.setValue("owmfail", false);
            } else {
                data = null;
                System.println("Response: " + responseCode);            // print response code
            }
            Background.exit(data);
        }

        function makeRequest() as Void {
            var url = "">api.openweathermap.org/.../onecall";                         // set the url
            // var url = "">api.openweathermap.org/.../weather";  
            System.println("Request URL: " + url);
            // Strg.setValue("owmfail", true);
            var latlon = null;
            var imperial = null;
            var OWM_API_KEY = null;
            try{
                latlon = Strg.getValue("LatLon");
                imperial = Strg.getValue("IMPERIAL");
                OWM_API_KEY = Strg.getValue("OWMAPI");
            }catch (e) {return;}
            if(latlon == null){
                System.println("Missing LatLon data!");
                return;
            }
            else{
                System.println("Lat: " + latlon[0]);
                System.println("Lon: " + latlon[1]);
            }
            if(latlon[0] == 0.0 && latlon[1] == 0.0){
                return;
            }
            if(OWM_API_KEY == null)
            {
                return;
            }
            var params = null;
            if(!imperial || imperial == null){
                params = {                                              // set the parameters
                    "lat" => latlon[0],
                    "lon" => latlon[1],
                    "exclude" => "minutely,hourly",
                    "units" => "metric",
                    // "lang" => "de",
                    "appid" => OWM_API_KEY
                };
            }
            else{
                params = {                                              // set the parameters
                    "lat" => latlon[0],
                    "lon" => latlon[1],
                    "exclude" => "minutely,hourly",
                    "units" => "imperial",
                    // "lang" => "de",
                    "appid" => OWM_API_KEY
                };
            }

            var options = {                                             // set the options
                :method => Communications.HTTP_REQUEST_METHOD_GET,      // set HTTP method
                :headers => {                                           // set headers
                "Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED},
                // set response type
                :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
            };
           
            var responseCallback = method(:onReceive);                  // set responseCallback to
            // onReceive() method
            // Make the Communications.makeWebRequest() call
            System.println("Requesting...");
            Communications.makeWebRequest(url, params, options, method(:onReceive));
        }
    }
  • Just a quick look, but if you don't have the latlon, don't do the makeWebRequest at all.  In my OWM stuff, if there's no APIKEY or latlon, I skip even requesting the data. Looks like you're doing the same but only for the API key

  • It's really weird, if I check if the api key is null or "" the if statement is always true... 

    And I can't use the string methods like size() on it. What does a Storage.getValue() return when it has never been set? I would suppose null? 

    Thank you so much! 

  • Is your API Key in Application.Storage or Application.Properties?  It would be in Properties if it's an app setting.  The only way it would get into Application.Storage is if your app itself put it there.

    an API key is a 32 character String so length() would work.

    And remember, you don't use "=" with stings.  You use equals()

    if(APIKEY!=null && APIKEY.length()==32) {} //api key is worth a try

    if(laton!=null) {} //latlon is work a try

    Also when you get lat and lon, use .toFloat() on them as there's been an issue with using Doubles in a makeWebRequest.

  • Thank you so much! Guess what... I found it.

    But it was the most obvious of all: I am on the new epix beta 8.13 - of course I only tried to install my own watch face to debug it.

    But it happens with any ciq watch face. They all crash on the first install - people probably don't see it because it just doesn't bring up the newly installed watchface and once you manually select it, it works.

    I tried it with the amazing watch faces of SHN and they also crash.

    I spent 4 hours plus commenting out all my code step by step until I arrived at a bare watch face with no function and the error was still there.

    Again thank you so much for all your input, I still found some little odds and quirks that I was able to iron out thanks to your hints! 

  • So glad I found this thread before I went too far down the rabbit hole. Same issue install new watchface, when it auto starts it shows the CIQ error screen (on a watchface I'm working on and on one jim_m_58 created. I'm wondering if this error is the watch firing up the watchface before a default settings file exists on device?