From 37b9c0899ff213dc91c0492ca357a22c29e3f2c7 Mon Sep 17 00:00:00 2001 From: annguyenNous Date: Fri, 26 Jun 2026 08:23:00 +0700 Subject: [PATCH] fix(tools,cli,gateway): add encoding="utf-8" to subprocess.run with text=True subprocess.run(..., text=True) without explicit encoding uses the system locale, which varies across platforms (Windows defaults to cp1252/mbcs, Linux to UTF-8). This causes mojibake on Windows when processing UTF-8 output from ffmpeg, whisper, and other tools. Add encoding="utf-8" to 11 subprocess.run calls across 8 files. --- hermes_cli/main.py | 2 +- hermes_cli/setup.py | 2 +- skills/creative/comfyui/scripts/auto_fix_deps.py | 2 +- tools/environments/singularity.py | 2 +- tools/transcription_tools.py | 6 +++--- tools/tts_tool.py | 2 +- tools/voice_mode.py | 4 ++-- tui_gateway/server.py | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index b4176f13e1646..e23365046bd4d 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -1155,7 +1155,7 @@ def _probe_container(cmd: list, backend: str, via_sudo: bool = False): all other exceptions propagate naturally. """ try: - return subprocess.run(cmd, capture_output=True, text=True, timeout=15) + return subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", timeout=15) except subprocess.TimeoutExpired: label = f"sudo {backend}" if via_sudo else backend print( diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 8eea7248d478d..6ccb4b36bc4c3 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -1440,7 +1440,7 @@ def setup_terminal_backend(config: dict): ssh_cmd.extend(["-p", port]) ssh_cmd.append(f"{user}@{host}" if user else host) ssh_cmd.append("echo ok") - result = subprocess.run(ssh_cmd, capture_output=True, text=True, timeout=10) + result = subprocess.run(ssh_cmd, capture_output=True, text=True, encoding="utf-8", timeout=10) if result.returncode == 0: print_success(" SSH connection successful!") else: diff --git a/skills/creative/comfyui/scripts/auto_fix_deps.py b/skills/creative/comfyui/scripts/auto_fix_deps.py index 788bf8e9e3bf4..ec643b3b6505f 100755 --- a/skills/creative/comfyui/scripts/auto_fix_deps.py +++ b/skills/creative/comfyui/scripts/auto_fix_deps.py @@ -51,7 +51,7 @@ def run_cmd(cmd: list[str], *, dry_run: bool = False) -> tuple[int, str]: if dry_run: return 0, "[dry-run]" log(f"$ {' '.join(cmd)}") - proc = subprocess.run(cmd, capture_output=True, text=True, check=False) + proc = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", check=False) out = (proc.stdout or "") + (proc.stderr or "") return proc.returncode, out diff --git a/tools/environments/singularity.py b/tools/environments/singularity.py index 666d908b2568f..2db6f07ea515b 100644 --- a/tools/environments/singularity.py +++ b/tools/environments/singularity.py @@ -220,7 +220,7 @@ def _start_instance(self): cmd.extend([str(self.image), self.instance_id]) try: - result = subprocess.run(cmd, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) + result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", timeout=120, stdin=subprocess.DEVNULL) if result.returncode != 0: raise RuntimeError(f"Failed to start instance: {result.stderr}") self._instance_started = True diff --git a/tools/transcription_tools.py b/tools/transcription_tools.py index d0712c81e1eb5..71510eeb93344 100644 --- a/tools/transcription_tools.py +++ b/tools/transcription_tools.py @@ -1187,7 +1187,7 @@ def _prepare_local_audio(file_path: str, work_dir: str) -> tuple[Optional[str], command = [ffmpeg, "-y", "-i", file_path, converted_path] try: - subprocess.run(command, check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) + subprocess.run(command, check=True, capture_output=True, text=True, encoding="utf-8", timeout=300, stdin=subprocess.DEVNULL) return converted_path, None except subprocess.TimeoutExpired: logger.error("ffmpeg conversion timed out for %s", file_path) @@ -1233,9 +1233,9 @@ def _transcribe_local_command(file_path: str, model_name: str) -> Dict[str, Any] # User-provided templates (env var) may contain shell syntax; auto-detected commands are safe for list mode. use_shell = bool(os.getenv(LOCAL_STT_COMMAND_ENV, "").strip()) if use_shell: - subprocess.run(command, shell=True, check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) + subprocess.run(command, shell=True, check=True, capture_output=True, text=True, encoding="utf-8", timeout=300, stdin=subprocess.DEVNULL) else: - subprocess.run(shlex.split(command), check=True, capture_output=True, text=True, timeout=300, stdin=subprocess.DEVNULL) + subprocess.run(shlex.split(command), check=True, capture_output=True, text=True, encoding="utf-8", timeout=300, stdin=subprocess.DEVNULL) txt_files = sorted(Path(output_dir).glob("*.txt")) diff --git a/tools/tts_tool.py b/tools/tts_tool.py index d803086983e0b..d654fe4bc278c 100644 --- a/tools/tts_tool.py +++ b/tools/tts_tool.py @@ -1859,7 +1859,7 @@ def _generate_neutts(text: str, output_path: str, tts_config: Dict[str, Any]) -> "--device", device, ] - result = subprocess.run(cmd, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) + result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8", timeout=120, stdin=subprocess.DEVNULL) if result.returncode != 0: stderr = result.stderr.strip() # Filter out the "OK:" line from stderr diff --git a/tools/voice_mode.py b/tools/voice_mode.py index d000e29d59d96..dc71efddc0ae2 100644 --- a/tools/voice_mode.py +++ b/tools/voice_mode.py @@ -389,7 +389,7 @@ def start(self, on_silence_stop=None) -> None: "-c", str(CHANNELS), ] try: - subprocess.run(command, capture_output=True, text=True, timeout=15, check=True, stdin=subprocess.DEVNULL) + subprocess.run(command, capture_output=True, text=True, encoding="utf-8", timeout=15, check=True, stdin=subprocess.DEVNULL) except subprocess.CalledProcessError as e: details = (e.stderr or e.stdout or str(e)).strip() raise RuntimeError(f"Termux microphone start failed: {details}") from e @@ -406,7 +406,7 @@ def _stop_termux_recording(self) -> None: mic_cmd = _termux_microphone_command() if not mic_cmd: return - subprocess.run([mic_cmd, "-q"], capture_output=True, text=True, timeout=15, check=False, stdin=subprocess.DEVNULL) + subprocess.run([mic_cmd, "-q"], capture_output=True, text=True, encoding="utf-8", timeout=15, check=False, stdin=subprocess.DEVNULL) def stop(self) -> Optional[str]: with self._lock: diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 0d572065d4471..666084a2f9409 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -8993,7 +8993,7 @@ def _(rid, params: dict) -> dict: str(pdf_path), str(out_prefix), ] try: - res = subprocess.run(argv, capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) + res = subprocess.run(argv, capture_output=True, text=True, encoding="utf-8", timeout=120, stdin=subprocess.DEVNULL) except subprocess.TimeoutExpired: return _err(rid, 5028, "pdftoppm timed out (>120s)") if res.returncode != 0: