Skip to content

Bug: MessageDecoder.decode discards default failure result when no plugin decodes #426

@kevinelliott

Description

@kevinelliott

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions