Unexpected Type Error when reading application properties

I've got this code 

enableNotifications = Application.Properties.getValue("enableNotifications");

Which on some devices appears to cause an unexpected type error, according to the ERA tool.

I can't tell why, and it also isn't something that a try/catch handles. Any thoughts on what it is? Originally the key I used for lookup was a constant string variable, I switched to the 

literal to see if it made a difference, but it didn't.

Any ideas?

  • See the new developer FAQ, specufucally, readKeyInt.  I alwayus use a wrapper function like is to check type.

    forums.garmin.com/.../new-developer-faq

  • For further context, sometimes a property (tied to a setting) can unexpectedly come back as null (even when you have a default) or as a string when you are expecting an integer or float. The latter situation was a bug in the Android Garmin Connect app at one point. (I think the specific bug was related to floats)

    The situation where it unexpectedly comes back as null happened when the user entered invalid data (e.g. a number out of the min/max range), Garmin Connect would set the corresponding property to null, instead of either:

    - setting the property to its previous (valid) value

    or

    - setting the property to the default value (which imo would be the most reasonable thing to do, assuming Garmin Connect didn't force the user to enter a valid value, which it didn't)

    The practical outcome of this is that you have to use a wrapper function as Jim suggested, *and* you have to also maintain a 2nd copy of your app settings defaults in the app code itself (if you want to handle the null case in a consistent way).

    I'm not sure if that quirk still exists, but it wouldn't surprise me, as it was raised as a bug / design issue a long time ago and Garmin never followed up on it afaik.

    Also, there are several errors that can't be caught by a try/catch, including unexpected type errors and array out of bounds errors.

    Try/catch only catches literal exceptions. (Whereas several kinds of errors returned by the API are not exceptions, but fatal errors which have to be proactively avoided.)

  • Huh, that wrapper function cited converts to a number, my setting is a boolean value. There isn't a 'ToBoolean()' method, maybe comparing the value against "true" or "false" would achieve that.

  • Sure, assuming that you want the default value to be the opposite of whatever value you're comparing to.

    A more generic way to do it would be have a wrapper which allows you to pass in the desired default (in case of null).

    However, if you can reproduce the problem or get help from affected users, you might want to use System.println() to see what the unexpected value actually is.

    (I can't imagine it would be anything other than null, though.)

    Here's my original bug report for numbers being returned as null. (The actual trigger was when the user entered a non-numeric value for a numeric setting). https://forums.garmin.com/developer/connect-iq/f/discussion/7666/gc-mobile-ios-setting-returned-as-null-if-non-numeric-entered-for-numeric

    Someone else reported a similar error with numbers (but they could not recreate it). Possibly the exact same bug:

    https://forums.garmin.com/developer/connect-iq/f/discussion/231603/era-report-can-only-be-replicated-with-brute-force-any-known-issues-with-settings

    I'm unaware of any issue with booleans tho. (I only use guard code for numeric values, to avoid wasting memory. I don't use it for enum settings, for example.)

    Might be worth digging into the issue.

    Or just create a wrapper function and be done with it.

  • What is interesting is that I haven't seen any crashes reported from code where I just use the return value from reading the property in a boolean context. I.e,  if (Application.Properties.getValue("someProperty")) { }

    Of course, I also can't repro this crash locally, only seems to happen on devices out in the wild, so I might be guessing, but it seems like there's potentially some type coercion happening in the conditional statement.

  • Yeah, well Monkey C has an "interesting" design decision / implementation where the following code is okay:

        var x = null;
        if (x) { // no crash here
            // ...
        }

    But this code will crash:

        var x = null;
        
    if (true && x) { // this will crash
            // ...
        }

    IOW, null will only be coerced to boolean in some circumstances and not others.

    To me, the real question is how did your boolean property become null in the first place?

    But it may not be worth trying to track down unless you can reproduce it yourself.

  • I'm just going to put guardrails around the assignment of the property value to the boolean state value, and ensure that I read a boolean before assigning, and otherwise ignore it. My guess is that that will prevent a recurrence.

  • Instead of "instanceof Number", use "instanceof Boolean"

  • instead of

    if(true & x)

    you really want

    if(true && x!=null)

  • I realize that, since I was the one who reported that bug / quirk years ago.

    The point wasn't to show code that I would write (nobody would write "if (true ...)" in the first place), my point was to show a bug / quirk in the language and to address the following comment:

    What is interesting is that I haven't seen any crashes reported from code where I just use the return value from reading the property in a boolean context. I.e,  if (Application.Properties.getValue("someProperty")) { }

    And to explain why that code would work (assuming the property value was null), but other code would crash.

    I also think that most would not expect "if (x)" and "if (true && x)" to behave differently. Surely either both would work or both would fail.

    I don't think it's great language design where null is automatically coerced to boolean in one case, and in another it case, attempting to do so causes a crash. Especially since it isn't documented, and Monkey C is a duck typed language, so the only way to find out is it to try. (I realize static types are coming in 4.0, but I'm referring to the state of Monkey C now and in the past.)