A header that declares an unknown or invalid MIME charset (e.g. a malformed
or spam Subject like =?x-unknown-charset?B?...?=) raised an uncaught
LookupError. bytes.decode(..., errors="replace") only handles byte-decode
errors, not codec *lookup* failures, so the "replace" safety net did not
apply.
_decode_header decodes Subject/From/To/Cc for the inbox list, single-message
fetch, and the background mail pollers (routes/email_routes.py,
routes/email_pollers.py, src/builtin_actions.py), so a single bad message
could crash the whole inbox render or the poller loop.
Wrap the per-part decode in try/except (LookupError, ValueError) and fall
back to utf-8/replace. Valid charsets (utf-8, iso-8859-1, ...) are unchanged.
Adds tests/test_email_decode_header.py — the unknown-charset case fails
before this change and passes after.