fix: release Paramiko import traceback
Paramiko caches its optional invoke import error with the original traceback, retaining the code-execution tool-loading stack and first agent. Clear only the stale traceback while preserving the exception, and add focused regression coverage.
Alessandro committed
Jul 21, 2026 at 00:56 UTC
0ddb5fb31d2abd1b0aeac80138c1dc58d64241fb
2 files changed
+18
plugins/_code_execution/helpers/shell_ssh.py
+4
@@ -7,6 +7,10 @@ from helpers.log import Log
7
from helpers.print_style import PrintStyle
8
# from helpers.strings import calculate_valid_match_lengths
9
10
+# Paramiko caches this optional import error; its traceback retains our tool-loading stack.
11
+if paramiko.config.invoke_import_error:
12
+ paramiko.config.invoke_import_error.__traceback__ = None
13
+
14
15
# Injected into every new SSH shell to keep it safe for non-interactive use.
16
# Pagers (more/less) would otherwise block forever waiting for input that
tests/test_code_execution_pager.py
+14
@@ -5,6 +5,7 @@ code execution tool: without user input they block forever and spin at 100% CPU.
5
"""
6
7
import asyncio
8
+import importlib
9
from types import SimpleNamespace
10
11
from plugins._code_execution.helpers import shell_local, shell_ssh
@@ -43,6 +44,19 @@ def test_ssh_command_disables_pagers():
44
assert "PAGER=cat" in shell_ssh.PAGER_DISABLE_COMMAND
45
46
47
+def test_paramiko_import_error_does_not_retain_tool_loading_stack(monkeypatch):
48
+ try:
49
+ raise ImportError("invoke")
50
+ except ImportError as error:
51
+ saved_error = error
52
+ monkeypatch.setattr(shell_ssh.paramiko.config, "invoke_import_error", error)
53
+
54
+ importlib.reload(shell_ssh)
55
+
56
+ assert shell_ssh.paramiko.config.invoke_import_error is saved_error
57
+ assert saved_error.__traceback__ is None
58
+
59
+
60
def test_multiline_terminal_commands_are_one_current_shell_compound():
61
assert _group_multiline_command("pwd") == "pwd"
62
assert _group_multiline_command("cd /tmp\npwd") == "{\ncd /tmp\npwd\n}"