Summary
All three Label_2P_FM* plugins try to detect a missing FM3 / FM4 / FM5 preamble with if (header.length == 0) after splitting parts[0]. String.prototype.split always returns at least one element, so the guard is never true — it's dead code. When the preamble token actually is missing, header[1] is undefined:
- FM3 dereferences
header[1].length → throws TypeError: Cannot read properties of undefined (reading 'length'). Because the dispatch in MessageDecoder.decode does not wrap individual plugins in try/catch, the exception propagates out and aborts the whole decode.
- FM4 and FM5 silently pass
undefined to ResultFormatter.departureAirport, producing a "successfully decoded" result with departureAirport: { value: undefined }.
All three plugins are reachable from MessageDecoder for label 2P (see MessageDecoder.ts:40-42), and any 2P message whose comma-count matches the plugin's expectation but whose parts[0] doesn't actually contain the FMx token hits this path.
This is not a duplicate of #459 (different file, different handlers).
Affected code
Same shape in three files. FM3:
// lib/plugins/Label_2P_FM3.ts:24-40
if (parts.length === 7) {
const header = parts[0].split('FM3 ');
if (header.length == 0) { // never true
ResultFormatter.unknown(decodeResult, message.text);
decodeResult.decoded = false;
decodeResult.decoder.decodeLevel = 'none';
return decodeResult;
}
if (header[0].length > 0) {
ResultFormatter.unknown(decodeResult, header[0].substring(0, 4));
ResultFormatter.flightNumber(decodeResult, header[0].substring(4));
}
if (header[1].length === 4) { ... } // throws when header[1] is undefined
FM4 (lib/plugins/Label_2P_FM4.ts:23-37) and FM5 (lib/plugins/Label_2P_FM5.ts:23-34) have the same dead guard and then pass header[1] straight into ResultFormatter.departureAirport without checking.
Note FM3 and FM5 split on 'FM3 '/'FM5 ' (with trailing space) while FM4 splits on 'FM4' (no space). The fix is the same shape in all three.
Reproduction
const decoder = new MessageDecoder();
// FM3 throws
decoder.decode({
label: '2P',
text: 'noFMheader,1454,N 45.206,E 17.726,34030, 440,98',
});
// → TypeError: Cannot read properties of undefined (reading 'length')
// FM4 silently produces undefined fields (10-field text, no 'FM4' token)
decoder.decode({
label: '2P',
text: 'noFMheader,KORD,2114000,1518,N 45.206,E 17.726,34030,180,foo,bar',
});
// → decoded: true, but result includes departureAirport.value === undefined
// FM5 silently produces undefined fields (12-field text, no 'FM5 ' token)
decoder.decode({
label: '2P',
text: 'noFMhdr,KORD,142000,1518,N 45.206,E 17.726,34030,a,b,c,FLT123,d',
});
// → decoded: true, but result includes departureAirport.value === undefined
Suggested fix
Change the guard in each plugin to check for the case the comment actually describes — the FMx token wasn't present:
if (header.length < 2) { // or: if (parts[0].indexOf('FM3 ') === -1)
ResultFormatter.unknown(decodeResult, message.text);
decodeResult.decoded = false;
decodeResult.decoder.decodeLevel = 'none';
return decodeResult;
}
(Replace 'FM3 ' / 'FM4' / 'FM5 ' accordingly per file.)
Test coverage
The existing Label_2P_FM3 test only covers well-formed FM3 … input, so the FM3 throw is uncovered. Adding a "no preamble" case per plugin that asserts the result has decoded === false and (for FM3) that the call does not throw would lock the fix in.
Summary
All three
Label_2P_FM*plugins try to detect a missingFM3/FM4/FM5preamble withif (header.length == 0)after splittingparts[0].String.prototype.splitalways returns at least one element, so the guard is never true — it's dead code. When the preamble token actually is missing,header[1]isundefined:header[1].length→ throwsTypeError: Cannot read properties of undefined (reading 'length'). Because the dispatch inMessageDecoder.decodedoes not wrap individual plugins intry/catch, the exception propagates out and aborts the whole decode.undefinedtoResultFormatter.departureAirport, producing a "successfully decoded" result withdepartureAirport: { value: undefined }.All three plugins are reachable from
MessageDecoderfor label2P(seeMessageDecoder.ts:40-42), and any 2P message whose comma-count matches the plugin's expectation but whoseparts[0]doesn't actually contain the FMx token hits this path.This is not a duplicate of #459 (different file, different handlers).
Affected code
Same shape in three files. FM3:
FM4 (
lib/plugins/Label_2P_FM4.ts:23-37) and FM5 (lib/plugins/Label_2P_FM5.ts:23-34) have the same dead guard and then passheader[1]straight intoResultFormatter.departureAirportwithout checking.Note FM3 and FM5 split on
'FM3 '/'FM5 '(with trailing space) while FM4 splits on'FM4'(no space). The fix is the same shape in all three.Reproduction
Suggested fix
Change the guard in each plugin to check for the case the comment actually describes — the FMx token wasn't present:
(Replace
'FM3 '/'FM4'/'FM5 'accordingly per file.)Test coverage
The existing
Label_2P_FM3test only covers well-formedFM3 …input, so the FM3 throw is uncovered. Adding a "no preamble" case per plugin that asserts the result hasdecoded === falseand (for FM3) that the call does not throw would lock the fix in.