mpremote fails with UnicodeEncodeError on Windows legacy consoles (cp1252)
Port, board and/or hardware
Windows (any hardware) - affects mpremote tool when run on Windows with legacy console (cp1252 or similar encoding).
MicroPython version
- mpremote 1.27.0
- Python 3.11.9 /3.13.1 (host)
- Tested against MicroPython 1.27.0 RP2, ESP32
Reproduction
This issue only occurs with legacy Windows consoles that use cp1252 or similar encodings. Modern terminals (Windows Terminal, VS Code) use UTF-8 by default and are not affected.
To reproduce, you must use a legacy console:
-
Open legacy cmd.exe (not Windows Terminal) or configure PowerShell to use cp1252:
# Force legacy encoding in Python $env:PYTHONIOENCODING = "cp1252" -
Create test files with non-ASCII characters:
mkdir unicode_test echo "test" > "unicode_test\Владимир_Петров.txt" -
Use mpremote to copy:
mpremote cp -rv unicode_test :
Alternatively, the issue can be demonstrated with this Python snippet:
import sys
sys.stdout.reconfigure(encoding='cp1252')
print('Владимир_Петров.txt') # Raises UnicodeEncodeError
Expected behaviour
mpremote cp should successfully copy files with Unicode characters in filenames on Windows. The tool should handle all Unicode characters in console output without raising encoding errors.
CPython's print() should use UTF-8 or properly handle the console encoding.
Observed behaviour
The command fails immediately with:
UnicodeEncodeError: 'charmap' codec can't encode characters in position X-Y: character maps to <undefined>
The file is not copied.
Additional Information
Workaround
Set the PYTHONIOENCODING environment variable before running mpremote:
$env:PYTHONIOENCODING = "utf-8"
mpremote connect COM3 cp -r . :
Or add to PowerShell $PROFILE for persistence:
$env:PYTHONIOENCODING = "utf-8"
$env:PYTHONUTF8 = "1"
Suggested Fix
Force UTF-8 encoding in mpremote on Windows:
import sys
import io
if sys.platform == 'win32':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
This would be a minimal change in the mpremote initialization code.
Code of Conduct
Yes, I agree
mpremote: Fix disconnect handling on Windows and Linux.
Summary
Improves device disconnection handling in mpremote to show a clean "device disconnected" message instead of stack traces on Windows and fixes terminal formatting issues on Linux.
Problem
When a device disconnects during an mpremote session:
- Windows: Shows a full stack trace with ClearCommError failed or similar errors
- Linux: 'device disconnected' with unclear terminal formatting, causing indentation issues
Solution
This PR fixes disconnect handling to:
- Catch serial disconnection exceptions in both console.py and repl.py
- Properly exit terminal raw mode before showing the message
- Display a clean "device disconnected" message after terminal cleanup
- Ensure consistent behavior across Windows and Linux platforms
File Changes
tools/mpremote/mpremote/console.py:
- Windows: Modified waitchar() to catch SerialException and re-raise with cleaner message
- Added proper exception handling for ClearCommError failed cases
tools/mpremote/mpremote/repl.py:
- Modified do_repl_main_loop() to catch serial exceptions and return disconnect state
- Added checks for both Windows and Linux disconnect error patterns
tools/mpremote/mpremote/main.py:
- Modified main loop to handle disconnect state after console cleanup
- Added newline and message printing after terminal mode is restored
Testing
Before/After Examples - I connected to ESP32S2 (usb serial) and once confirmed the repl is working, physically unplug the devices.
Windows (Before)
PS C:\Users\anl> mpremote
Connected to MicroPython at COM7
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.25.0-1.ge7b0fe4775.dirty on 2025-05-09; Generic ESP32S2 module with ESP32S2
Type "help()" for more information.
>>>
>>> Traceback (most recent call last):
File "C:\Users\anl\AppData\Roaming\uv\python\cpython-3.10.11-windows-x86_64-none\lib\runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "C:\Users\anl\AppData\Roaming\uv\python\cpython-3.10.11-windows-x86_64-none\lib\runpy.py", line 86, in _run_code
exec(code, run_globals)
File "c:\users\anl\.local\bin\mpremote.exe\__main__.py", line 8, in <module>
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\mpremote\main.py", line 584, in main
disconnected = do_repl(state, argparse_repl().parse_args([]))
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\mpremote\repl.py", line 102, in do_repl
disconnected = do_repl_main_loop(
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\mpremote\repl.py", line 11, in do_repl_main_loop
console_in.waitchar(state.transport.serial)
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\mpremote\console.py", line 99, in waitchar
while not (self.inWaiting() or pyb_serial.inWaiting()):
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\serial\serialutil.py", line 594, in inWaiting
return self.in_waiting
File "C:\Users\anl\AppData\Roaming\uv\tools\mpremote\lib\site-packages\serial\serialwin32.py", line 259, in in_waiting
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
serial.serialutil.SerialException: ClearCommError failed (PermissionError(13, 'The device does not recognize the command.', None, 22))
PS C:\Users\anl>
Windows (After)
PS C:\Users\anl> mpremote
Connected to MicroPython at COM7
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.25.0-1.ge7b0fe4775.dirty on 2025-05-09; Generic ESP32S2 module with ESP32S2
Type "help()" for more information.
>>>
>>>
>>>
device disconnected
PS C:\Users\anl>
Linux (Before)
anl@ANL2-LAP:~/micropython/tools/mpremote$ mpremote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.25.0-1.ge7b0fe4775.dirty on 2025-05-09; Generic ESP32S2 module with ESP32S2
Type "help()" for more information.
>>>
>>> device disconnected
anl@ANL2-LAP:~/micropython/tools/mpremote$
Linux (After)
anl@ANL2-LAP:~/micropython/tools/mpremote$ mpremote
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.25.0-1.ge7b0fe4775.dirty on 2025-05-09; Generic ESP32S2 module with ESP32S2
Type "help()" for more information.
>>>
>>>
device disconnected
anl@ANL2-LAP:~/micropython/tools/mpremote$
- Tested on Windows 11 with ESP32S2 USB serial
- Tested on Linux (Ubuntu WSL) with ESP32S2 USB serial
- Verified terminal formatting is restored properly
- Confirmed clean exit without stack traces