Summary
MessageDecoder.decode() builds a sensible default result for the "no plugin matched" case (with error: 'No known decoder plugin for this message', the original message, full remaining.text, and decoder: { name: 'none', ... }), but then unconditionally overwrites that result with the return value of every plugin in the loop. If no plugin sets decoded: true, callers get the last plugin's return value — which has none of the diagnostic fields.
Affected code
lib/MessageDecoder.ts — the decode loop:
let result: DecodeResult = {
decoded: false,
error: 'No known decoder plugin for this message',
decoder: { name: 'none', type: 'none', decodeLevel: 'none' },
message: message,
remaining: { text: message.text },
raw: {},
formatted: { description: 'Not Decoded', items: [] },
};
for (let i = 0; i < usablePlugins.length; i++) {
const plugin = usablePlugins[i];
result = plugin.decode(message, options); // overwrites the default
if (result.decoded) {
break;
}
}
return result;
When the loop finishes without a successful decode, result is whatever the last plugin returned. Per DecoderPlugin.defaultResult(), that result has:
- no
error field
- no
message field
decoder.name set to the plugin name (e.g. "label-44-pos"), suggesting falsely that that plugin handled the message
formatted.description set to "Unknown" rather than "Not Decoded"
Reproduction
Pass a message with a registered label whose payload doesn't match any plugin's pattern (e.g. label 44 with garbage text). The returned decoder.name will be the last Label_44_* plugin tried, and error / message will be missing.
Suggested fix
Track plugin returns in a temporary and only overwrite the default on a successful decode (or merge the diagnostic fields):
for (const plugin of usablePlugins) {
const r = plugin.decode(message, options);
if (r.decoded) {
result = r;
break;
}
}
return result;
This also matches the "no plugin matched" semantics that the default object clearly intends.
Summary
MessageDecoder.decode()builds a sensible defaultresultfor the "no plugin matched" case (witherror: 'No known decoder plugin for this message', the originalmessage, fullremaining.text, anddecoder: { name: 'none', ... }), but then unconditionally overwrites thatresultwith the return value of every plugin in the loop. If no plugin setsdecoded: true, callers get the last plugin's return value — which has none of the diagnostic fields.Affected code
lib/MessageDecoder.ts— the decode loop:When the loop finishes without a successful decode,
resultis whatever the last plugin returned. PerDecoderPlugin.defaultResult(), that result has:errorfieldmessagefielddecoder.nameset to the plugin name (e.g."label-44-pos"), suggesting falsely that that plugin handled the messageformatted.descriptionset to"Unknown"rather than"Not Decoded"Reproduction
Pass a message with a registered label whose payload doesn't match any plugin's pattern (e.g. label
44with garbage text). The returneddecoder.namewill be the lastLabel_44_*plugin tried, anderror/messagewill be missing.Suggested fix
Track plugin returns in a temporary and only overwrite the default on a successful decode (or merge the diagnostic fields):
This also matches the "no plugin matched" semantics that the default object clearly intends.