Hi,
I ran into a second crash in #setAccumulatedField in the JS SDK. It's related to this thread but has a different cause. The .components fix from this thread is already in 21.217.0. This one still happens there, with the default read() options and without includeUnknownData.
Affected version: 21.217.0 (latest)
Error: TypeError: Cannot read properties of undefined (reading '0')
Trigger: a message definition contains a field with components that target an accumulated field, and that accumulated field itself, in that order. Real-world example: a Forerunner 255 triathlon file whose swim records contain both cycles (field 18, component → total_cycles) and total_cycles (field 19, accumulated).
Cause: in src/decoder.js, #setAccumulatedField (around line 725 in 21.217.0):
value = (((value / field.scale) - field.offset) + containingField.offset[i]) * containingField.scale[i];
containingField comes from Object.values(message), so it's the decoded value entry { rawFieldValue, fieldDefinitionNumber }, not the profile field. It has no offset/scale arrays.
Impact: read() aborts and every following message is lost. With files that write the summary first, the sessions are decoded but every record is lost.
Minimal reproduction:
import { Decoder, Encoder, Profile, Stream } from "@garmin/fitsdk";
const encoder = new Encoder();
encoder.onMesg(Profile.MesgNum.FILE_ID, {
type: "activity",
manufacturer: "development",
product: 0,
timeCreated: new Date("2024-01-01T00:00:00Z"),
});
for (let i = 0; i < 3; i++) {
encoder.onMesg(Profile.MesgNum.RECORD, {
timestamp: new Date(Date.UTC(2024, 0, 1, 0, 0, i)),
cycles: 10 + i,
totalCycles: 10 + i,
});
}
const { messages, errors } = new Decoder(Stream.fromByteArray(encoder.close())).read();
console.log(messages.recordMesgs.length, errors.map(String));
// 21.217.0: 0 [ "TypeError: Cannot read properties of undefined (reading '0')" ]
Suggested fix: look up the profile field of the containing field:
if (targetField?.num == field.num && targetField?.isAccumulated) {
- value = (((value / field.scale) - field.offset) + containingField.offset[i]) * containingField.scale[i];
+ const containingProfileField = messageDefinition.fields[containingField.fieldDefinitionNumber];
+ value = (((value / field.scale) - field.offset) + containingProfileField.offset[i]) * containingProfileField.scale[i];
}
With this change the reproduction decodes all 3 records (cycles/totalCycles 10, 11, 12), and the triathlon file decodes completely (4043 records, no errors).
Other affected field pairs: in the profile, the same crash can occur for:
record: cycles → total_cycles
record: compressed_speed_distance → distance
record: compressed_accumulated_power → accumulated_power
hr: event_timestamp_12 → event_timestamp
Thanks!