Two independent correctness bugs surface on Windows. Neither mechanism is
Windows-specific — both reproduce on macOS/Linux once the same condition is
forced — and both were found by the Windows CI job added in #582.
1. Any command dies when stdout cannot encode a character it prints
optics doctor aborts before printing a single row:
Error: 'charmap' codec can't encode character '\U0001fa7a' in position 0
U+1FA7A is the 🩺 in optics_framework/helper/doctor.py. On Windows
sys.stdout is UTF-8 only when attached to a real console; under CI, a pipe or
a redirect, Python falls back to the locale encoding (cp1252). rich's
safe_box degrades the box drawing to ASCII but does not transcode text, so
the first unencodable character raises UnicodeEncodeError and the command
exits without producing its diagnostic.
Reproduction (no Windows runner needed)
Forcing the encoding reproduces the reported failure verbatim on macOS:
$ PYTHONIOENCODING=cp1252 optics doctor > out.txt
Error: 'charmap' codec can't encode character '\U0001fa7a' in position 0: character maps to <undefined>
*** You may need to add PYTHONIOENCODING=utf-8 to your environment ***
$ echo $?
3
Reduced to six lines:
import io
from rich.console import Console
stream = io.TextIOWrapper(io.BytesIO(), encoding="cp1252", errors="strict")
Console(file=stream, force_terminal=False).print("[bold]\U0001fa7a optics doctor[/bold]\n")
# UnicodeEncodeError: 'charmap' codec can't encode character '\U0001fa7a'
It is systemic, not one line
13 shipped modules contain characters cp1252 cannot represent —
doctor.py (🩺 plus the ✅/⚠️/❌ on every row), onboarding.py (👋),
error.py (❌ in the error panel), runner/printers.py (→),
live_tui.py, quickstart.py, setup.py, config_manager.py,
ai_self_heal.py, async_utils.py, live.py,
engines/drivers/playwright.py, engines/elementsources/playwright_page_source.py.
Patching the doctor banner only moves the crash to the next line. Exercising
seven of those paths through one cp1252 stream fails seven times out of seven
on main.
LC_ALL=C on Linux gives an ascii stdout with the identical result, so this
is not only a Windows problem.
2. Screenshots are silently dropped when the filename is rejected
utils.save_screenshot (optics_framework/common/utils.py) interpolates the
caller-supplied time_stamp into the filename without sanitising it:
screenshot_file_path = os.path.join(output_dir, f"{time_stamp}-{name}.jpg")
utils.get_timestamp() returns ISO-8601 —
2026-09-22T16:51:50.397462+05:30 — whose colons are reserved in a Windows
filename. api/verifier.py passes exactly that value for every annotated
presence assertion, and api/action_keyword.py does the same for both
annotated-result screenshots.
Note the asymmetry: the default timestamp branch already uses a
Windows-safe %H-%M-%S-%f, and name is already sanitised. Only the
passed-in timestamp is not, so the bug is invisible in the common path and
hits precisely the captures that carry detection annotations.
It fails silently. cv2.imwrite reports a rejected path by returning
False rather than raising, and the call sits inside a try/except, so the
capture vanishes while the run still exits 0 and reports a pass. A run that
loses its evidence and claims success is worse than one that fails.
Expected
- The filename contains none of
<>:"/\|?* regardless of the timestamp handed in.
- A write that did not happen is logged at a level a user sees. It should stay
non-fatal — screenshots are diagnostic output — but it must not be invisible.
Impact
The Windows CI job in #582 cannot go green while (1) stands, and (2) means any
Windows run has been quietly discarding its annotated captures.
Two independent correctness bugs surface on Windows. Neither mechanism is
Windows-specific — both reproduce on macOS/Linux once the same condition is
forced — and both were found by the Windows CI job added in #582.
1. Any command dies when stdout cannot encode a character it prints
optics doctoraborts before printing a single row:U+1FA7Ais the 🩺 inoptics_framework/helper/doctor.py. On Windowssys.stdoutis UTF-8 only when attached to a real console; under CI, a pipe ora redirect, Python falls back to the locale encoding (
cp1252). rich'ssafe_boxdegrades the box drawing to ASCII but does not transcode text, sothe first unencodable character raises
UnicodeEncodeErrorand the commandexits without producing its diagnostic.
Reproduction (no Windows runner needed)
Forcing the encoding reproduces the reported failure verbatim on macOS:
Reduced to six lines:
It is systemic, not one line
13 shipped modules contain characters
⚠️ /❌ on every row),
cp1252cannot represent —doctor.py(🩺 plus the ✅/onboarding.py(👋),error.py(❌ in the error panel),runner/printers.py(→),live_tui.py,quickstart.py,setup.py,config_manager.py,ai_self_heal.py,async_utils.py,live.py,engines/drivers/playwright.py,engines/elementsources/playwright_page_source.py.Patching the doctor banner only moves the crash to the next line. Exercising
seven of those paths through one
cp1252stream fails seven times out of sevenon
main.LC_ALL=Con Linux gives anasciistdout with the identical result, so thisis not only a Windows problem.
2. Screenshots are silently dropped when the filename is rejected
utils.save_screenshot(optics_framework/common/utils.py) interpolates thecaller-supplied
time_stampinto the filename without sanitising it:utils.get_timestamp()returns ISO-8601 —2026-09-22T16:51:50.397462+05:30— whose colons are reserved in a Windowsfilename.
api/verifier.pypasses exactly that value for every annotatedpresence assertion, and
api/action_keyword.pydoes the same for bothannotated-result screenshots.
Note the asymmetry: the default timestamp branch already uses a
Windows-safe
%H-%M-%S-%f, andnameis already sanitised. Only thepassed-in timestamp is not, so the bug is invisible in the common path and
hits precisely the captures that carry detection annotations.
It fails silently.
cv2.imwritereports a rejected path by returningFalserather than raising, and the call sits inside atry/except, so thecapture vanishes while the run still exits 0 and reports a pass. A run that
loses its evidence and claims success is worse than one that fails.
Expected
<>:"/\|?*regardless of the timestamp handed in.non-fatal — screenshots are diagnostic output — but it must not be invisible.
Impact
The Windows CI job in #582 cannot go green while (1) stands, and (2) means any
Windows run has been quietly discarding its annotated captures.