From de0cfe028ac83a5db25192a3a6739e0dc32250f6 Mon Sep 17 00:00:00 2001 From: VectorPeak <73048950+VectorPeak@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:09:39 +0800 Subject: [PATCH 1/2] fix: decode shell skill output as UTF-8 --- src/google/adk/tools/skill_toolset.py | 1 + tests/unittests/tools/test_skill_toolset.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/google/adk/tools/skill_toolset.py b/src/google/adk/tools/skill_toolset.py index c09b2dcdda..5b6ca4ccd5 100644 --- a/src/google/adk/tools/skill_toolset.py +++ b/src/google/adk/tools/skill_toolset.py @@ -745,6 +745,7 @@ def _build_wrapper_code( " _r = subprocess.run(", f" {arr!r},", " capture_output=True, text=True,", + " encoding='utf-8', errors='replace',", f" timeout={timeout!r}, cwd=td,", " )", " print(_json.dumps({", diff --git a/tests/unittests/tools/test_skill_toolset.py b/tests/unittests/tools/test_skill_toolset.py index 450dbe3f30..f85480b85f 100644 --- a/tests/unittests/tools/test_skill_toolset.py +++ b/tests/unittests/tools/test_skill_toolset.py @@ -996,6 +996,8 @@ async def test_execute_script_shell_success(mock_skill1): code_input = call_args[0][1] assert "subprocess.run" in code_input.code assert "bash" in code_input.code + assert "encoding='utf-8'" in code_input.code + assert "errors='replace'" in code_input.code assert "__shell_result__" in code_input.code From 182ec0fb7e3f80e2ff38887e7ebc60a78bb25089 Mon Sep 17 00:00:00 2001 From: VectorPeak <73048950+VectorPeak@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:59:54 +0800 Subject: [PATCH 2/2] fix: preserve shell exit code with stderr output --- src/google/adk/tools/skill_toolset.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/google/adk/tools/skill_toolset.py b/src/google/adk/tools/skill_toolset.py index 5b6ca4ccd5..7709726776 100644 --- a/src/google/adk/tools/skill_toolset.py +++ b/src/google/adk/tools/skill_toolset.py @@ -564,8 +564,13 @@ async def execute_script_async( stdout = parsed.get("stdout", "") stderr = parsed.get("stderr", "") rc = parsed.get("returncode", 0) - if rc != 0 and not stderr: - stderr = f"Exit code {rc}" + if rc != 0: + exit_code_message = f"Exit code {rc}" + stderr = ( + f"{stderr.rstrip()}\n{exit_code_message}" + if stderr + else exit_code_message + ) except (json.JSONDecodeError, ValueError): pass @@ -745,6 +750,7 @@ def _build_wrapper_code( " _r = subprocess.run(", f" {arr!r},", " capture_output=True, text=True,", + # Keep shell output decoding independent from the host locale. " encoding='utf-8', errors='replace',", f" timeout={timeout!r}, cwd=td,", " )",