Skip to content

.NET: Python: [Bug]: persistent PowerShell shell session reports a stale exit code for cmdlet-only commands #8205

Closed

Description

Description

On Windows, LocalShellTool(mode="persistent") reports the exit code of an earlier command for any later command that only runs cmdlets.

ShellSession._build_script derives the return code from $LASTEXITCODE:

"   Invoke-Expression $__af_cmd;"
"   if ($LASTEXITCODE -ne $null) { $__af_rc = $LASTEXITCODE }"
"   elseif (-not $?) { $__af_rc = 1 }"

$LASTEXITCODE is a session-wide automatic variable that PowerShell only updates when a native (external) executable exits. Cmdlets leave it untouched. Since the session is long-lived, once any command in it has run an external process, $LASTEXITCODE stays set, and every subsequent cmdlet-only command takes the if branch and reports that stale value instead of 0.

The blast radius is that the tool tells the agent a command failed when it succeeded, for the whole rest of the session, until another external command happens to reset the variable.

Stateless mode is unaffected — each command is a fresh process and the rc comes from the process exit code.

Code Sample

import asyncio
from agent_framework_tools.shell._resolve import resolve_shell
from agent_framework_tools.shell._session import ShellSession

async def main():
    async with ShellSession(resolve_shell(None, interactive=True)) as s:
        for cmd in [
            "Write-Output 'cmdlet only'",
            "cmd /c exit 3",
            "Write-Output 'cmdlet only, after a failing native command'",
            "Write-Output 'cmdlet only again'",
        ]:
            r = await s.run(cmd, timeout=20)
            print(f"exit_code={r.exit_code:<4} cmd={cmd}")

asyncio.run(main())

Output I get on Windows 11 with Windows PowerShell 5.1:

exit_code=0    cmd=Write-Output 'cmdlet only'
exit_code=3    cmd=cmd /c exit 3
exit_code=3    cmd=Write-Output 'cmdlet only, after a failing native command'
exit_code=3    cmd=Write-Output 'cmdlet only again'

The last two commands succeeded and printed their output, but are reported as exit code 3.

Expected: 0, 3, 0, 0.

Error Messages / Stack Traces

No exception — the wrong value is returned silently in ShellResult.exit_code.

Package Versions

agent-framework-tools: 1.0.0b260730 (from a source checkout of main at 992d445)

Python Version

Python 3.12.9

Additional Context

This is Windows-only, and the CI matrix (.github/workflows/python-*.yml) is ubuntu-latest everywhere, so the existing PowerShell tests in python/packages/tools/tests/test_local_shell_tool.py — the ones guarded by @pytest.mark.skipif(sys.platform != "win32", ...) — never actually execute anywhere.

The .NET port has the same lines in dotnet/src/Microsoft.Agents.AI.Tools.Shell/ShellSession.cs (around line 592), so it very likely has the same behaviour. I have not built or run the .NET side, so I am reporting that as an observation rather than a verified claim.

Activity

  1. added
    .NETUsage: [Issues, PRs], Target: .Net
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 9, 2026
  2. changed the title [-]Python: [Bug]: persistent PowerShell shell session reports a stale exit code for cmdlet-only commands[/-] [+].NET: Python: [Bug]: persistent PowerShell shell session reports a stale exit code for cmdlet-only commands[/+] on Sep 9, 2026
  3. added
    reproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow
    on Sep 9, 2026
  4. github-actions commented on Sep 9, 2026

    @github-actions
    Contributor

    🤖 Automated triage reproduction notes (agent-authored — trust but verify)

    Agent analysis

    Repro: python/packages/tools/agent_framework_tools/shell/_session.py::ShellSession._build_script at lines 342-365 reports a stale exit code when a persistent PowerShell session runs a failing native process followed by a successful cmdlet-only command. Minimal repro: start ShellSession with non-interactive pwsh, run pwsh -NoProfile -Command 'exit 3', then run Write-Output 'cmdlet only'; the second result contains the expected output but has exit_code=3. The equivalent $LASTEXITCODE-first logic is also present in dotnet/src/Microsoft.Agents.AI.Tools.Shell/ShellSession.cs::BuildScript at lines 592-593.

    • Failing test: python/packages/tools/tests/test_local_shell_tool.py::test_persistent_powershell_does_not_reuse_native_exit_code
    • Files examined: python/packages/tools/agent_framework_tools/shell/_session.py, python/packages/tools/agent_framework_tools/shell/_resolve.py, python/packages/tools/tests/test_local_shell_tool.py, python/packages/tools/pyproject.toml, dotnet/src/Microsoft.Agents.AI.Tools.Shell/ShellSession.cs, dotnet/tests/Microsoft.Agents.AI.Tools.Shell.UnitTests/ShellSessionTests.cs
    • Tests run: python/packages/tools/tests/test_local_shell_tool.py -k persistent_powershell, python/packages/tools/tests/test_local_shell_tool.py::test_persistent_powershell_does_not_reuse_native_exit_code
    • Reported version: 1.0.0b260730
    • Current version: 1.0.0b260730
  5. added
    skillsUsage: [Issues, PRs], Target: skills related features
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

.NETUsage: [Issues, PRs], Target: .NetpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflowskillsUsage: [Issues, PRs], Target: skills related features

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions