Summary
The loader script's send_loader_error() function POSTs to the telemetry endpoint with a payload that is missing two fields that the binary's full telemetry payload always includes: agent_version and user_identity. When a device's binary install/scan never completes successfully (download failure, checksum mismatch, sandbox restrictions, scan crash, etc.), the loader-error pings are the only telemetry the backend ever sees for that device. The dashboard then renders these rows with agent_version = null (UI fallback: "vunknown") and an empty/unknown user identity.
Where it happens
send_loader_error() in the loader scripts (delivered server-side from the dashboard, mirrored across the platform variants — confirmed in the Linux loader; the macOS and Windows loaders share the same shape per the loader header notes). Current Linux payload:
{
"customer_id": "...",
"device_id": "...",
"collected_at": ...,
"hostname": "...",
"os_version": "...",
"platform": "linux",
"loader_errors": [{"error_type": "...", "message": "..."}]
}
For comparison, the binary's main telemetry payload (internal/telemetry/telemetry.go Payload struct) and reportRunStatus() payload (internal/telemetry/run_status.go) both include agent_version: buildinfo.Version and (in the main payload) user_identity. Only the loader's error path is missing them.
Symptom on the dashboard
Rows for affected devices display:
Dev Machine Guard Version: vunknown — the dashboard renders versions as "v" + agent_version, with "unknown" as the falsy fallback when the field is null/missing.
User: empty / unknown.
- Often (but separately)
Device ID is a UUID-shaped string rather than a hardware serial, because the affected fleet tends to be virtualized Macs where IOPlatformSerialNumber itself is a UUID.
The three symptoms cluster on the same rows because they share a population: virtualized / CI / headless Mac fleets where the binary's full telemetry path is more likely to fail (so loader-errors are the only thing that lands), the OS reports synthetic identifiers (so device_id is a UUID), and there is no GUI console user to populate user_identity even if the binary did run (see #63).
Root cause
The loader knows both fields at the time it fires send_loader_error():
BINARY_VERSION is hardcoded near the top of the loader script (it's the version the loader is about to download / has just failed to download). Plumbing it into the payload is a one-line addition.
LOGGED_IN_USER is resolved earlier in the script (in the user-resolution block that handles SUDO_USER / loginctl / $USER / who). Same: one-line addition.
Both fields are simply omitted from the JSON body in send_loader_error().
Suggested fix
Add the two fields to the send_loader_error() curl payload. Linux loader patch:
curl -s -X POST \
-H "Authorization: Bearer ${API_KEY}" \
-H "Content-Type: application/json" \
- -d "{\"customer_id\":\"${CUSTOMER_ID}\",\"device_id\":\"${device_id}\",\"collected_at\":${now},\"hostname\":\"$(json_escape "$host")\",\"os_version\":\"$(json_escape "$os_ver")\",\"platform\":\"linux\",\"loader_errors\":[{\"error_type\":\"${error_type}\",\"message\":\"$(json_escape "$message")\"}]}" \
+ -d "{\"customer_id\":\"${CUSTOMER_ID}\",\"device_id\":\"${device_id}\",\"collected_at\":${now},\"hostname\":\"$(json_escape "$host")\",\"os_version\":\"$(json_escape "$os_ver")\",\"platform\":\"linux\",\"agent_version\":\"${BINARY_VERSION}\",\"user_identity\":\"$(json_escape "$LOGGED_IN_USER")\",\"loader_errors\":[{\"error_type\":\"${error_type}\",\"message\":\"$(json_escape "$message")\"}]}" \
"${API_ENDPOINT}/v1/${CUSTOMER_ID}/developer-mdm-agent/telemetry" \
>/dev/null 2>&1 || true
Mirror in the macOS and Windows loader variants. Two-line change per platform.
If the backend has a strict schema for this endpoint, also confirm it accepts the new fields without 400-ing the loader's error pings (otherwise the loader telemetry path silently breaks across all current installs the moment the new schema rolls out). Best to make the backend permissive of the new fields first, then update loaders.
Verification
After the fix:
- Devices that hit a loader-error path (binary download fails, checksum mismatch, etc.) will show up on the dashboard with the correct
vN.N.N version (whatever version the loader was trying to install) and the resolved local user, instead of vunknown / blank.
- Reduces apparent fleet metadata-quality issues without requiring backend or dashboard changes.
Additional context
Third in a series of silent-degradation bugs in the install/telemetry surface:
None of these are hard failures — they all let the install/telemetry pipeline keep moving — but they compound on the same fleets (virtualized / CI / headless Macs) and make those rows look like "no metadata" on the dashboard. Worth fixing together as a metadata-quality pass.
Summary
The loader script's
send_loader_error()function POSTs to the telemetry endpoint with a payload that is missing two fields that the binary's full telemetry payload always includes:agent_versionanduser_identity. When a device's binary install/scan never completes successfully (download failure, checksum mismatch, sandbox restrictions, scan crash, etc.), the loader-error pings are the only telemetry the backend ever sees for that device. The dashboard then renders these rows withagent_version = null(UI fallback:"vunknown") and an empty/unknown user identity.Where it happens
send_loader_error()in the loader scripts (delivered server-side from the dashboard, mirrored across the platform variants — confirmed in the Linux loader; the macOS and Windows loaders share the same shape per the loader header notes). Current Linux payload:{ "customer_id": "...", "device_id": "...", "collected_at": ..., "hostname": "...", "os_version": "...", "platform": "linux", "loader_errors": [{"error_type": "...", "message": "..."}] }For comparison, the binary's main telemetry payload (
internal/telemetry/telemetry.goPayloadstruct) andreportRunStatus()payload (internal/telemetry/run_status.go) both includeagent_version: buildinfo.Versionand (in the main payload)user_identity. Only the loader's error path is missing them.Symptom on the dashboard
Rows for affected devices display:
Dev Machine Guard Version:vunknown— the dashboard renders versions as"v" + agent_version, with"unknown"as the falsy fallback when the field is null/missing.User: empty / unknown.Device IDis a UUID-shaped string rather than a hardware serial, because the affected fleet tends to be virtualized Macs whereIOPlatformSerialNumberitself is a UUID.The three symptoms cluster on the same rows because they share a population: virtualized / CI / headless Mac fleets where the binary's full telemetry path is more likely to fail (so loader-errors are the only thing that lands), the OS reports synthetic identifiers (so
device_idis a UUID), and there is no GUI console user to populateuser_identityeven if the binary did run (see #63).Root cause
The loader knows both fields at the time it fires
send_loader_error():BINARY_VERSIONis hardcoded near the top of the loader script (it's the version the loader is about to download / has just failed to download). Plumbing it into the payload is a one-line addition.LOGGED_IN_USERis resolved earlier in the script (in the user-resolution block that handlesSUDO_USER/loginctl/$USER/who). Same: one-line addition.Both fields are simply omitted from the JSON body in
send_loader_error().Suggested fix
Add the two fields to the
send_loader_error()curl payload. Linux loader patch:curl -s -X POST \ -H "Authorization: Bearer ${API_KEY}" \ -H "Content-Type: application/json" \ - -d "{\"customer_id\":\"${CUSTOMER_ID}\",\"device_id\":\"${device_id}\",\"collected_at\":${now},\"hostname\":\"$(json_escape "$host")\",\"os_version\":\"$(json_escape "$os_ver")\",\"platform\":\"linux\",\"loader_errors\":[{\"error_type\":\"${error_type}\",\"message\":\"$(json_escape "$message")\"}]}" \ + -d "{\"customer_id\":\"${CUSTOMER_ID}\",\"device_id\":\"${device_id}\",\"collected_at\":${now},\"hostname\":\"$(json_escape "$host")\",\"os_version\":\"$(json_escape "$os_ver")\",\"platform\":\"linux\",\"agent_version\":\"${BINARY_VERSION}\",\"user_identity\":\"$(json_escape "$LOGGED_IN_USER")\",\"loader_errors\":[{\"error_type\":\"${error_type}\",\"message\":\"$(json_escape "$message")\"}]}" \ "${API_ENDPOINT}/v1/${CUSTOMER_ID}/developer-mdm-agent/telemetry" \ >/dev/null 2>&1 || trueMirror in the macOS and Windows loader variants. Two-line change per platform.
If the backend has a strict schema for this endpoint, also confirm it accepts the new fields without 400-ing the loader's error pings (otherwise the loader telemetry path silently breaks across all current installs the moment the new schema rolls out). Best to make the backend permissive of the new fields first, then update loaders.
Verification
After the fix:
vN.N.Nversion (whatever version the loader was trying to install) and the resolved local user, instead ofvunknown/ blank.Additional context
Third in a series of silent-degradation bugs in the install/telemetry surface:
enable --nowtimer races with inline post-install telemetry #62 — Linux install lock-race (enable --nowtimer fires immediately and races with inline post-install telemetry).LoggedInUser()silently falls through toroot, masking missing-user metadata in telemetry #63 — macOSLoggedInUser()silently falls through torootunder LaunchDaemon when no console user is detected.agent_versionanduser_identity, so devices that never complete a binary scan show up with degraded metadata even though both values are known to the loader at error time.None of these are hard failures — they all let the install/telemetry pipeline keep moving — but they compound on the same fleets (virtualized / CI / headless Macs) and make those rows look like "no metadata" on the dashboard. Worth fixing together as a metadata-quality pass.