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.

  • As settings are represented for app as dictionary, badKey:

    1.  ok

    2. no global variable - failed invoking <symbol = global dictionary>

    3. there is the dictionary but without badKey

    In PSX-7 I use 2 'hidden" properties and some users have errors, when trying to read the first of them:

    Error Name: Unhandled Exception
    Occurrences: 18
    First Occurrence: 2024-06-08
    Last Occurrence: 2024-07-15
    Devices:
        fēnix® 6 Pro / 6 Sapphire / 6 Pro Solar / 6 Pro Dual Power / quatix® 6: 27.00
        Venu® 3: 10.20
        Forerunner® 165: 19.18
        fēnix® 7 Pro: 17.28
    App Versions: 3.8.0
    Languages: cht, deu, eng, pol

    Error Name: Unhandled Exception
    Occurrences: 3
    First Occurrence: 2024-06-20
    Last Occurrence: 2024-06-20
    Devices:
        vívoactive® 4S: 8.30
    App Versions: 3.8.0
    Languages: ces

    My watch f7 15.77, CIQ 4.2.4 - there is no error. So it means it's possible to use hidden properties.

    I don't use try/catch because I could catch only InvalidKeyException but key should always exist.

    Unexpected Type Error suggest that system tries to read value form settings, compare to type in properties/settings and it can't be converted to it, so it means:

    - dev's error - exception should be thrown every time

    - ge/connect still saves bad values

  • I'm pretty sure that the GE/GCM can save garbage data. For example in my app I can reproduce it by opening the settings, not changing anything, and saving. So both the types and the values (range of number, length of string) are correct, and still.

    But today I played with this a bit and I'm getting to the conclusion that this happens only when there's not enough memory. I can also reproduce the following: start the app in sim, open the settings, change 1 value, save it. I get the popup telling me that it was successfully saved. However when I close the settings, then open it again I don't see the value I saved, but the default that was set in the properties.xml.

    A minute ago I was able to do the above, seemingly successfully saved, but then when I reopen the settings I get:

    It also seems to depend on the "order" of the settings / properties. For example when I changed the 1st setting number from 0 to 1 it was saved, and it was still 1 when I reopened the settings.

    When I changed another number from 0 to 1 that is further down in the settings, then it caused the above "error 500"...

  • Found something interesting. I added log to the getConfig, so I print the key and the value I got. When onSettingChanged is called I read all the config into variables, so it prints put all the configs.

    Interestingly it prints that all the values are null! This is when the "seemingly successful" save of the settings wasn't actually successful (obviously) and then when I reopen settings it displays the 500 error.

    What is even more interesting is that I now removed the try/catch, so I should be seeing the Unexpected Type Error, but instead I am seeing all my properties are read one by one, and their value is null, so the null value doesn't even trigger the Unexpected Type Error.

    In case someone asks, all these are in the simulator.

  • I've discovered it years ago...

    https://forums.garmin.com/developer/connect-iq/i/bug-reports/still-problem-with-memory-and-settings

    the problem is when you say settings and system need additional memory - why? nobody knows, it's enough to delete old var before create the new one, but system probably create the new object

    the funny thing is when no memory system deletes the old var...

  • My guess is this: there are (maybe) 2 ways the settings can be saved (on a real device at least):

    1. when the app isn't running

    2. when the app is running

    I would've think that in order to save the same settings either way the same amount of memory should be enough. In the sim I can only test when the app is running, but it looks like the amount of memory the running app is using eats too much memory, so there's not enough to save the settings. I guess, that all the settings dictionary and it's creation need to fit into the memory.

  • Interestingly it prints that all the values are null! This is when the "seemingly successful" save of the settings wasn't actually successful (obviously) and then when I reopen settings it displays the 500 error.

    Yeah I saw a similar bug / undesired behavior years ago — before the Connect IQ store app even existed — when Connect failed to save settings bc the user made an error such as typing in non-numerical characters in a numerical field. Instead of preventing the user from typing the forbidden characters or preventing the user from submitting the form, Connect just sent nulls for everything while *displaying* default values to the user after refreshing the form.

    This meant that if your setting had defaults and you wanted to preserve them under all circumstances (including the “all my settings are now null -_-“ scenario), you actually had to store a 2nd set of defaults in application code to be used when a setting was read as null. This obviously sucked for various reasons, such as the violation of DRY (don’t repeat yourself) and wasting memory for the additional code.

    At the very least, I would’ve hoped that Connect would send default values instead of nulls, since it displayed default values after the refresh, but that was too much to ask I guess. Another alternative would be to allow the app to access default settings  programmatically, but nope, that’s not possible either.

    I naively hoped we were beyond that kind of bug now, but I guess not.

  • My guess is this: there are (maybe) 2 ways the settings can be saved (on a real device at least):

    1. when the app isn't running

    2. when the app is running

    I would've think that in order to save the same settings either way the same amount of memory should be enough. In the sim I can only test when the app is running, but it looks like the amount of memory the running app is using eats too much memory, so there's not enough to save the settings. I guess, that all the settings dictionary and it's creation need to fit into the memory.

    Yeah I have definitely had data field apps that crash when settings are saved while the app is running, especially on old devices where memory was very tight. In the sim this would manifest as the app crashing and all settings being returned to default values.

    Tbh I never bothered to handle that case bc:

    - i’m a runner, my apps are for runners, and I think it’s very rare for a runner to change app settings in the middle of a run. (I personally don’t even bring my phone with me when I run.)

    - it would’ve taken additional code to be able to handle app settings changes after the app was initialized (I noticed that some popular data fields did not support this either)

    Whether or not the existing design / implementation “makes sense”, it’s just another example of suboptimal behavior. First of all, app properties from settings take a huge amount of memory relative to available RAM, especially for old devices (but even for some recent-ish devices like vivoactive4). Not only that, but to support saving settings during app execution, you probably need to reserve an equal of amount of slack memory just so the app doesn’t crash. Neither of those things would be a problem if Garmins had a ton of memory available for CIQ apps, but they didn’t.

  • I've shown consuming memory in the bug report. You can see it yourself, just see peek of memory in memory viewer before and after saving properties, even you don't change anything. When there is no memory settings dictionary disappeares.