Unexpected Type Error

I read lots of the old threads about Unexpected Type Error and how Properties.getValue can return null. My problem resembles that, but I still can't figure out what is the problem.

My code is:

function getConfig(key as PropertyKeyType) as PropertyValueType? {
  var val; // this is the line I get the error according to ERA
  try {
    val = Properties.getValue(key);
  } catch (e) {
    logRelease(key + ":" + e.getErrorMessage());
    val = null;
  }
  return val;
}

And this is the error from ERA:

Error Name: Unexpected Type Error
Occurrences: 7
First Occurrence: 2024-01-17
Last Occurrence: 2024-01-22
Devices:
    vívoactive® 4S: 7.80
App Versions: 2.3.1
Languages: pol
Backtrace:
    Config.getConfig:18
    Config.getConfigNumber:93
    MyApp.onStop:122 

So what I know is that the error is in getConfig. And before you point me to the best practices to check for null and type, all that code is in getConfigNumber().
Because the call comes from MyApp.onStop:122, I even know that key = "i" so I believe even the
key + ":" + e.getErrorMessage()
can't be the problem (all 3 are strings)

However I can't understand then what can be the problem in the last line that makes sense:
val = Properties.getValue(key);

the value of the i property is supposed to be a number, but even if it isn't (and that would be taken care of in getConfigNumber) whatever the type of the property is, including null, I can't understand how the above line (or any other line in getConfig() can cause an Unexpected Type Error.

  • With Application.Properties.getValue() an exception is thrown if the key doesn't exist.

    another is thrown if the type for the key is invalid (that's what you see).  Are you sure the key is always a string?  Maybe use instanceof to verify that, as using println can sometimes mask things.

  • Another thing I'd look at is not doing the setValue() in onStop, but while your code is still running when the value changes.  Things ate already shutting down before onStop is called, so the settings could already be gone.

  • The value is set in the settings by the user. It is supposed to be numeric:

        <setting title="@Strings.settingsAntId" prompt="@Strings.settingsAntIdPrompt" type="number">
          <settingConfig id="i" type="numeric" min="0" max="1048576"/>
        </setting>
    

    But let's assume that somehow the user (or a CIQ bug) managed to save there a string. Why would that throw an error in the code inserted above? (Again, I am dealing with invalid values in getConfigNumber)
    And most importantly how am I supposed to fix this? It's already in try {}, that doesn't seem to work.

  • Definitely passing “i” and not a variable i when calling getConfig? I only ask as i is often used in loops.

  • Yes. The code works for more than 140k users. I get a couple of tens of errors like this. Judging from the language and FW version it's from a handful of users.

  • I got back to investigate this further. I'll ask a question that maybe I should've asked 2 years ago:

    #properties.xml:
    
    <properties>
      <property id="i" type="number">0</property><!--requested ANT ID-->
      <property id="I" type="number">0</property><!--saved ANT ID-->
    </properties>
    
    
    
    #settings.xml:
    
    <settings>
      <setting propertyKey="@Properties.i" title="@Strings.settingsForceAntId">
        <settingConfig type="numeric" min="0" max="1048576"/>
      </setting>
    </settings>
    
    
    
    #config.mc:
    
    function getConfig(key as PropertyKeyType) as PropertyValueType? {
      var val;
      try {
        val = Properties.getValue(key); // here I get ERA: Unexpected Type Error
      } catch (e) {
        val = null;
      }
      return val;
    }
    
    function toConfigNumber(value as PropertyValueType?, defaultValue as Number) as Number {
        if (value instanceof Lang.Boolean) {
            return value ? 1 : 0;
        }
        // this should cover Float, Double, Number, String, Long, Char, Symbol
        value = value != null && value has :toNumber ? value.toNumber() : null;
        return value != null ? value as Number : defaultValue;
    }
    
    function getConfigNumber(key as PropertyKeyType, defaultValue as Number) as Number {
        return toConfigNumber(getConfig(key), defaultValue);
    }
    
    
    
    #App.mc:
    
    // triggered by settings change in GCM
    public function onSettingsChanged() as Void {
        var antId = getConfigNumber("i", 0);
        //...
    }

    1. note that I have 2 properties, one is small letter i, the other is capital letter I

    2. note that i is set from the GCM settings

    3. note that I isn't included in the settings.xml, it's only being written/read from code (and the reason of being a property is that I support apiLevel 1.2.0 and above, and of course I also have the code for older devices, that is not included above)


    Until now I interpreted the properties.xml the following way: when I install the app, then:
    either a) settings are not generated, but when they are read then the default given in properties.xml is returned
    or b) settings file is generated and saved during app installation with the default values given in properties.xml

    But now I wonder: did I misunderstand something? Am I not allowed to call Properties.getValue("i") before I called Properties.setValue("i", 0) ???

  • I think b) is correct (settings are generated based on priorities.xml that has to contain all properties) and you should read default value

  • See https://forums.garmin.com/developer/connect-iq/w/wiki/4/new-developer-faq#settings-crash

    On some devices, at least in the past, a Number can be seen as a String, which can generate a type error based on how it's used.

    I've used a function like you see there in every app since app-settings were introduced, With the current version, it's smarter as it handles devices with Application.Properties.getValue and not just the older getProperty call.

    Another "rule" I live by and have since app-settings were introduced, is that if I use a property, it's got an associated setting.  Also, changing the xml for this can cause the .set file to be reset.  You see a message about this in the sim, even if you just switch to a different target without changing the xml.

    In the debug console you'll see this:

    WARNING - persisted values reset due to settings redefinition.