Forerunner 965 firmware 27.09 rejects sideloaded watch-app with -400, SDK sample works

My Connect IQ app fails to launch on Forerunner 965 with error `-400`. The
SDK's bundled sample app builds with the same dev key + SDK and launches fine
on the same watch, so the environment is proven. I've exhausted the obvious
fixes and would appreciate a hint at what loader-level rejection reasons don't
produce a CIQ_LOG.TXT entry.

**Environment**
- Device: Forerunner 965, part `006-B4315-00`, firmware `27.09`, CIQ runtime
`6.0.0`
- SDK: Connect IQ SDK 9.1.0 (`connectiq-sdk-mac-9.1.0-2026-03-09-6a872a80b`)
- Dev key: `developer_key.der` — RSA 4096 PKCS#8 DER, same key used for the
working sample below
- Sideload: OpenMTP → `GARMIN/APPS/` → watch power-cycled after every transfer

**Baseline (rules out dev key / SDK / signing / sideload pipeline)**
I built the SDK-provided `samples/ApplicationStorage` with `-d fr965 -y
developer_key.der`, sideloaded it, and it launches and runs correctly.

**My app manifest (current state after fixes below)**
```xml
<?xml version="1.0"?>
<iq:manifest xmlns:iq="">www.garmin.com/.../connectiq" version="1">
<iq:application entry="AmakaFlowApp"
id="d79bef4b-8805-44f2-8cdb-9b784a3be996"
launcherIcon="@Drawables.LauncherIcon"
minSdkVersion="4.0.0" name="@Strings.AppName" type="watch-app">
<iq:products>
<iq:product id="fr965" />
<iq:product id="fenix847mm" />
</iq:products>
<iq:permissions>
<iq:uses-permission id="Communications" />
<iq:uses-permission id="Positioning" />
<iq:uses-permission id="PersistedContent" />
</iq:permissions>
<iq:languages>
<iq:language>eng</iq:language>
</iq:languages>
</iq:application>
</iq:manifest>

Build command
monkeyc -f monkey.jungle -o amakaflow.prg -d fr965 -y
~/.connectiq/developer_key.der -l 0 -w

Fixes I already tried, each sideloaded + power-cycled, all still -400
1. Added <iq:uses-permission id="PersistedContent" /> — matches the only real
error in the rotated CIQ_LOG.BAK (from an earlier session).
2. Changed type="widget" → type="watch-app".
3. Changed minApiLevel="3.2.0" → 4.0.0.
4. Removed all (:glance) annotations from app code, deleted GlanceView.mc.
5. Changed manifest version="5" → version="1", renamed minApiLevel →
minSdkVersion.
6. Rebuilt the SDK sample (ApplicationStorage) with my app's dashed UUID —
still -400, which rules out UUID format.

Logs
CIQ_LOG.TXT does not exist on the watch. Only CIQ_LOG.BAK from an earlier
session, containing PersistedContent permission errors (since fixed). I
believe the current -400 is happening before the CIQ runtime writes any log.

Questions
1. What loader-level rejection reasons on firmware 27.09 / CIQ 6.0.0 do not
produce a CIQ_LOG.TXT entry?
2. Is there a way to force the loader to emit a more specific rejection reason
(developer mode, verbose sideload, adb-style log)?
3. Did manifest / packaging / signing requirements change in a firmware update
between March and April 2026 that I should look at specifically?
4. Is there a monkeyc --validate or .prg inspector I can run locally to
confirm the package is well-formed?

Any pointers appreciated — happy to post additional diagnostic output.

  • The ApplicationStore sample you are using doesn't have the communications permission, but your app does. so I assume you are trying to do something like a makeWebRequest.  That, and -400 is a comm error.  (in the API guide and the Communications module, you can see the meaning of that code)

    Maybe you are exiting on the error and not crashing.

  • Update — resolved. Root cause was stale GARMINDEVICE.XML metadata keyed on
    ▎ our old app UUID from earlier failed sideloads. Regenerating the app UUID in
    ▎ the manifest + sideloading with a fresh .prg filename cleared it. The -400
    ▎ we were seeing was a loader-level rejection that happens before the CIQ
    ▎ runtime starts, which is why no CIQ_LOG.TXT is ever written (it's not the
    ▎ Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE runtime code — same
    ▎ number, different layer). On watch-app type and PersistedContent permission
    ▎ + removing widget-era (:glance) annotations were also needed, but the
    ▎ UUID/cache issue was the final unlock. Thanks for the pointer!