Skip to content

Fix UnicodeDecodeError on malformed UTF-8 radio responses#12

Merged
wlcrs merged 1 commit intozhelev:masterfrom
007hacky007:fix-unicode-decode-error
Apr 3, 2026
Merged

Fix UnicodeDecodeError on malformed UTF-8 radio responses#12
wlcrs merged 1 commit intozhelev:masterfrom
007hacky007:fix-unicode-decode-error

Conversation

@007hacky007
Copy link
Copy Markdown
Contributor

Summary

Some Frontier Silicon radios truncate text metadata (e.g. netRemote.play.info.text) at a fixed byte boundary, which can split a multi-byte UTF-8 character in half. This causes a UnicodeDecodeError in __call() at:

doc = ET.fromstring(await result.text(encoding="utf-8"))

This crashes the entire API call, making the device entity unavailable in downstream consumers like Home Assistant.

Root cause

The radio firmware returns text fields with a fixed-size buffer. When the text contains multi-byte UTF-8 characters (common in languages with diacritics - Czech, German, French, etc.), the firmware may cut the buffer mid-character.

For example, a real response from a Hama DIT2010 tuned to a Czech station (Expres FM):

00000120: 2074 7261 706e c3bd 206d 6f64 6572 c3a1   trapn.. moder..
00000130: 746f 7273 6bc3 bd20 7673 7475 7079 2061  torsk.. vstupy a
00000140: 2070 6f64 6f62 6ec3 3c2f 6338 5f61 7272   podobn.</c8_arr

The byte 0xc3 at position 0x14e is the start of a 2-byte UTF-8 sequence (e.g. c3 a9 = e), but the continuation byte is missing - the text was truncated right before the closing </c8_array> XML tag.

This produces:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 311: invalid continuation byte

Full traceback from Home Assistant:

File "homeassistant/components/frontier_silicon/media_player.py", line 157, in async_update
    info_text = await afsapi.get_play_text()
File "afsapi/api.py", line 414, in get_play_text
    return await self.handle_text(API["text"])
File "afsapi/api.py", line 274, in handle_text
    return unpack_xml(await self.handle_get(item), "value/c8_array")
File "afsapi/api.py", line 255, in handle_get
    return await self.__call(f"GET/{item}")
File "afsapi/api.py", line 220, in __call
    doc = ET.fromstring(await result.text(encoding="utf-8"))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 311: invalid continuation byte

Fix

Pass errors="replace" to aiohttp's response.text() so that malformed bytes are replaced with the Unicode replacement character (U+FFFD) instead of raising an exception. The text podobn\xc3 becomes podobn\ufffd - a single character is lost but the rest of the response (including the XML structure) is preserved.

Test plan

  • Tested against a live Hama DIT2010 radio at a station broadcasting Czech text metadata with diacritics that triggers truncation
  • Before fix: get_play_text() raises UnicodeDecodeError, device entity becomes unavailable
  • After fix: get_play_text() returns the text with a replacement character, device stays functional

@wlcrs wlcrs merged commit 1861ad4 into zhelev:master Apr 3, 2026
wlcrs added a commit to wlcrs/python-afsapi that referenced this pull request Apr 3, 2026
wlcrs pushed a commit to wlcrs/python-afsapi that referenced this pull request Apr 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants