Acknowledged

Watch face crash on some watches, it works on simulator.

My watch face (https://apps.garmin.com/en-US/apps/d44a137e-7a1e-4b67-9aa6-133d48db745c) is crashing when using one function on some watches, Forerunner 45 and Fenix 7s that I could get feedback from. When I try in the simulator for those watches, it works fine and it also works fine on other watches, like Forerunner 645 (that I own).

This is the code that is crashing, I tried to wrap up everything on try catches without luck. I am starting to get multiple complaints.

Here is the offending code, I was able to confirm the function getHRMax is working if used alone (via other functionality of my watch face).

    //! Get the VO2Max. Try first from watch, then calculate.
    //! @return [Float] Floating number with VO2Max approximation
    function getVO2Max() {
		var vo2max = null;
		var profile = null;

		try {
			profile = UserProfile.getProfile();
		}
		catch(ex) {
		}

		// RFCS-64 2022/7/24 - Check if can get VO2Max from watch directly
		if (null != profile && profile has :vo2maxCycling && profile has :vo2maxRunning) {
			try {
				// RFCS-64 2022/7/24 - Check current sport from watch
				if (UserProfile.HR_ZONE_SPORT_BIKING == UserProfile.getCurrentSport()) {				
					vo2max = profile.vo2maxCycling;
				}
				else { // RFCS-64 2022/7/24 - Any other sport, get running VO2Max
					vo2max = profile.vo2maxRunning;
				}
			}
			catch (ex) {
				// If could not get the watch VO2Max, try to calculate
				vo2max = RunFocusData.calculateVO2Max();
			}
		}
		else {
			// If version of the watch does not have VO2Max exposed, calculate
			vo2max = RunFocusData.calculateVO2Max();
		}

		return vo2max;
    }
    
    //! Calculate current VO2Max based on simple formula to give approximation
    //! VO2Max = ( HRmax / HRrest ) * 15.3
    //! @return [Float] Floating number with VO2Max approximation
    function calculateVO2Max() {
		var rhr = null;
		var hrmax = null;
		var vo2max = null;

		var profile = null;
		try {
			profile = UserProfile.getProfile();
		}
		catch(ex) {
		}
		finally {
			// RFCS-64 2022/7/24 - Watch API does not have VO2Max exposed, calculate
			if (null != profile) {
				try {
					rhr = profile.restingHeartRate;
				}
				catch (ex) {
				}
				finally {
					if (null != rhr) {
						try {
							hrmax = RunFocusData.getHRMax();
						}
						catch (ex) {
						}
						
						if (null != hrmax) {
							try {
								// RFCS-64 2022/7/24 - Make sure it converts to float
								vo2max = Math.round((hrmax.toFloat() / rhr) * 15.3);
							} catch (ex) {
								vo2max = null;
							}
						}
					}
				}
			}
		}
		
		return vo2max;
	}
    
	// RFCS-69 2022/7/24 - Trying to make sure it doesnt crash
    //! Get HRMax from HR zone 5
    //! @return [Number] HRMax
    function getHRMax() {
		var HRMax = null;
		var genericZoneInfo = null;
		try {
			genericZoneInfo = UserProfile.getHeartRateZones(UserProfile.HR_ZONE_SPORT_RUNNING);
			// RFCS-69 2022/7/24 - An array was returned and it has at least one element
			if (genericZoneInfo has :size && genericZoneInfo.size() > 0) {
				HRMax = genericZoneInfo[genericZoneInfo.size()-1];
			}
		}
		catch (ex) {
		}

		return HRMax;
    }

I wonder if anyone is experiencing the same or may have an idea of what is going on. It looks similar on reports about using Moment.subtract as reported here https://forums.garmin.com/developer/connect-iq/i/bug-reports/runtime-error-with-moment-subtract-when-subtracting-a-duration but maybe some other function?

Remember, it is working fine on simulator and other watches, only on some watches that it doesnt work.

  • Maybe it's a design/implementation inconsistency where some watches return 0 instead of null, when the RHR is invalid. It's a known design decision that UserProfile.restingHeartRate is initially invalid/null when it hasn't been set by the user.

    It is too bad that there's lots of errors that can't be caught in Monkey C. As a general rule, the only exceptions that are thrown in Monkey C are explicitly documented for a given method.

  • Thank you for your response.

    I added a check for resting HR and it is now working. All those try catches were added in a desperate hope that it would at least prevent the crashing. As you mentioned, it is Garmin, can't trust o anything, I was assuming two wrong things with Garmin, first that try catch would actually catch any errors, second that I was going to get a valid resting HR, never even imagined it could return zero.

    Apparently, that forum you pointed to was useless to have Garmin to fix the issue, they had only 3+ years to fix. I am sure it is probably something silly, like a variable not being properly assigned.

  • As you may have noticed, there are certain failure conditions in Connect IQ which are errors that can't be caught, as opposed to exceptions which can be caught.

    Division by zero is one of those errors. (When I try it in some test code, I get an "Invalid Value" error.)

    I noticed that you're dividing hrmax by rhr without first checking to see if rhr is 0. While technically this should be impossible, that's probably not a good assumption to make. I mean:

    1) This is Garmin we're talking about. I would never assume that there's no bugs, inconsistencies or otherwise unexpected data coming from the API.

    2) "be conservative in what you do, be liberal in what you accept from others". Unless you're 100% sure that the divisor can never be zero and you also need to squeeze every last byte of memory savings from your code, it would be good to proactively check for divide by zero errors in general. Given that you've changed your code to do a bunch of try...catches, I'm guessing you have memory to spare. (And let's be real, if your code is so tight that you literally can't add a divide by zero check, that's probably a cause for concern. Then again I have apps that are right on the edge lol)

    See this thread:

    restingHeartRate returns 0 on Vivoactive 3 (music)

    forums.garmin.com/.../restingheartrate-returns-0-on-vivoactive-3-music