diff --git a/client/ssh/client.go b/client/ssh/client.go index 7c0e90fbd..2775c8304 100644 --- a/client/ssh/client.go +++ b/client/ssh/client.go @@ -81,7 +81,8 @@ func (c *Client) handleSessionError(err error) error { } var e *ssh.ExitError - if !errors.As(err, &e) { + var em *ssh.ExitMissingError + if !errors.As(err, &e) && !errors.As(err, &em) { // Only return actual errors (not exit status errors) return fmt.Errorf("session wait: %w", err) } @@ -89,6 +90,7 @@ func (c *Client) handleSessionError(err error) error { // SSH should behave like regular command execution: // Non-zero exit codes are normal and should not be treated as errors // The command ran successfully, it just returned a non-zero exit code + // ExitMissingError is also normal - session was torn down cleanly return nil } @@ -116,12 +118,14 @@ func (c *Client) ExecuteCommand(ctx context.Context, command string) ([]byte, er output, err := session.CombinedOutput(command) if err != nil { var e *ssh.ExitError - if !errors.As(err, &e) { + var em *ssh.ExitMissingError + if !errors.As(err, &e) && !errors.As(err, &em) { // Only return actual errors (not exit status errors) return output, fmt.Errorf("execute command: %w", err) } // SSH should behave like regular command execution: // Non-zero exit codes are normal and should not be treated as errors + // ExitMissingError is also normal - session was torn down cleanly // Return the output even for non-zero exit codes } @@ -210,10 +214,14 @@ func (c *Client) handleCommandError(err error) error { } var e *ssh.ExitError - if !errors.As(err, &e) { + var em *ssh.ExitMissingError + if !errors.As(err, &e) && !errors.As(err, &em) { return fmt.Errorf("execute command: %w", err) } + // SSH should behave like regular command execution: + // Non-zero exit codes are normal and should not be treated as errors + // ExitMissingError is also normal - session was torn down cleanly return nil } diff --git a/client/ssh/client_test.go b/client/ssh/client_test.go index dd8642cf4..bd5e4c38c 100644 --- a/client/ssh/client_test.go +++ b/client/ssh/client_test.go @@ -613,7 +613,7 @@ func TestSSHClient_PipedCommand(t *testing.T) { // Test with piped commands that don't require PTY var pipeCmd string if runtime.GOOS == "windows" { - pipeCmd = "echo hello world | findstr hello" + pipeCmd = "echo hello world | Select-String hello" } else { pipeCmd = "echo 'hello world' | grep hello" } @@ -1112,9 +1112,9 @@ func TestBehaviorRegression(t *testing.T) { command string }{ {"simple echo", "echo test"}, - {"current directory", "cd"}, - {"list files", "dir"}, - {"system info", "systeminfo | findstr /B /C:\"OS Name\""}, + {"current directory", "Get-Location"}, + {"list files", "Get-ChildItem"}, + {"system info", "$PSVersionTable.PSVersion"}, } } else { testCases = []struct { @@ -1231,9 +1231,9 @@ func TestSSHClient_NonZeroExitCodes(t *testing.T) { name string command string }{ - {"findstr no match", "echo hello | findstr notfound"}, - {"exit 1 command", "cmd /c exit 1"}, - {"dir nonexistent", "dir C:\\nonexistent\\path"}, + {"select-string no match", "echo hello | Select-String notfound"}, + {"exit 1 command", "throw \"exit with code 1\""}, + {"get-childitem nonexistent", "Get-ChildItem C:\\nonexistent\\path"}, } } else { testCases = []struct {