From 69b402f8727d0087d2b8026504d6df0ff91b35c5 Mon Sep 17 00:00:00 2001 From: Smittix Date: Thu, 26 Feb 2026 23:02:07 +0000 Subject: [PATCH] fix: prevent APRS stream crash on invalid UTF-8 bytes from decoder Switch decoder subprocess from text mode to binary mode and decode each line with errors='replace' so corrupted radio bytes (e.g. 0xf7) are substituted instead of raising UnicodeDecodeError after long runs. Co-Authored-By: Claude Opus 4.6 --- routes/aprs.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/routes/aprs.py b/routes/aprs.py index a6e29d6..606f3a4 100644 --- a/routes/aprs.py +++ b/routes/aprs.py @@ -1462,9 +1462,11 @@ def stream_aprs_output(rtl_process: subprocess.Popen, decoder_process: subproces try: app_module.aprs_queue.put({'type': 'status', 'status': 'started'}) - # Read line-by-line in text mode. Empty string '' signals EOF. - for line in iter(decoder_process.stdout.readline, ''): - line = line.strip() + # Read line-by-line in binary mode. Empty bytes b'' signals EOF. + # Decode with errors='replace' so corrupted radio bytes (e.g. 0xf7) + # never crash the stream. + for raw in iter(decoder_process.stdout.readline, b''): + line = raw.decode('utf-8', errors='replace').strip() if not line: continue @@ -1784,15 +1786,15 @@ def start_aprs() -> Response: rtl_stderr_thread.start() # Start decoder with stdin wired to rtl_fm's stdout. - # Use text mode with line buffering for reliable line-by-line reading. + # Use binary mode to avoid UnicodeDecodeError on raw/corrupted bytes + # from the radio decoder (e.g. 0xf7). Lines are decoded manually + # in stream_aprs_output with errors='replace'. # Merge stderr into stdout to avoid blocking on unbuffered stderr. decoder_process = subprocess.Popen( decoder_cmd, stdin=rtl_process.stdout, stdout=PIPE, stderr=STDOUT, - text=True, - bufsize=1, start_new_session=True ) @@ -1827,7 +1829,8 @@ def start_aprs() -> Response: if decoder_process.poll() is not None: # Decoder exited early - capture any output - error_output = decoder_process.stdout.read()[:500] if decoder_process.stdout else '' + raw_output = decoder_process.stdout.read()[:500] if decoder_process.stdout else b'' + error_output = raw_output.decode('utf-8', errors='replace') if raw_output else '' error_msg = f'{decoder_name} failed to start' if error_output: error_msg += f': {error_output}'