ITADN

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

#426Openkevinelliott 创建于 2026-04-25
bug
K
kevinelliottcommented
## 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: ```ts 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): ```ts 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.
0 条评论